# Falcon native UDP (/falcon/udp)

> The exact Falcon UDP datagram frame for Solana transactions, every silent drop reason, and how to retry when nothing ever answers.



One signed Solana transaction per datagram, no envelope, and no response, for any reason. Everything you learn about the outcome, you learn from the signature on a normal Solana RPC.

You send to `<region>.falcon.wtf:9000`, so Frankfurt is `fra.falcon.wtf:9000`.

## Build the frame

Falcon accepts two frame formats and tells them apart by shape. We recommend the binary one: it's 21 bytes smaller and can't be misread.

### Binary frame (recommended)

```text
+----------------------+----------------------------------+
| 16-byte API key UUID | raw serialized transaction bytes |
+----------------------+----------------------------------+
   bytes 0..16            bytes 16..N
```

| Field       | Size                                                        |
| ----------- | ----------------------------------------------------------- |
| API key     | Exactly 16 bytes: the raw UUID, not the 36-character string |
| Transaction | 1 to 1,232 bytes                                            |

No length prefix, no version byte, no checksum, no base64, no delimiter.

### ASCII frame (alternative)

```text
+------------------------+-----------+----------------------------------+
| 36-char UUID text      | delimiter | raw serialized transaction bytes |
+------------------------+-----------+----------------------------------+
   bytes 0..36              byte 36     bytes 37..N
```

The delimiter is one of `:`, newline, space or tab.

That 1,232-byte cap applies to the **transaction portion only**; the UUID prefix and the delimiter don't count against it. So the largest legal datagram is 1,248 bytes binary, or 1,269 bytes ASCII.

Falcon only reads the ASCII form when all three hold: the datagram is longer than 36 bytes, byte 36 is a delimiter, and the first 36 bytes parse as a UUID. A binary frame that happens to carry a delimiter byte at offset 36 still parses correctly as binary.

[Telemetry](/falcon/telemetry) covers what not to record about these frames.

## Send a datagram from Node.js

```bash
export FALCON_UDP_HOST='fra.falcon.wtf'
export FALCON_UDP_PORT='9000'
export FALCON_API_KEY='00000000-0000-0000-0000-000000000000'
```

```javascript
import dgram from 'node:dgram';
import { readFile } from 'node:fs/promises';

const host = process.env.FALCON_UDP_HOST;
const port = Number(process.env.FALCON_UDP_PORT);
const apiKey = process.env.FALCON_API_KEY;

if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(apiKey ?? '')) {
  throw new Error('FALCON_API_KEY must be a UUID');
}

const apiKeyBytes = Buffer.from(apiKey.replaceAll('-', ''), 'hex'); // 16 bytes
const transactionBytes = await readFile('transaction.bin');

if (transactionBytes.length === 0 || transactionBytes.length > 1232) {
  throw new Error('Transaction must be 1 to 1232 bytes');
}

const packet = Buffer.concat([apiKeyBytes, transactionBytes]);
const socket = dgram.createSocket('udp4');

socket.send(packet, port, host, (error) => {
  socket.close();
  if (error) throw error;
});
```

The ASCII variant is the same code with a different prefix:

```javascript
const packet = Buffer.concat([Buffer.from(`${apiKey}:`, 'ascii'), transactionBytes]);
```

## Why a datagram is dropped

The reasons, in the order they're evaluated:

| Reason                    | Trigger                                                                                                                                                                                                    |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Empty frame               | Datagram is empty, or carries a key and no transaction bytes (exactly 16 bytes binary, exactly 37 bytes ASCII)                                                                                             |
| Missing API key           | Datagram shorter than 16 bytes                                                                                                                                                                             |
| Too large                 | Transaction portion exceeds 1,232 bytes                                                                                                                                                                    |
| Unauthorized key          | The key is not a live Falcon key. A malformed 36-character ASCII UUID lands here too rather than in its own reason: the datagram falls through to the binary reading, so its first 16 bytes become the key |
| Invalid transaction bytes | Will not deserialize, unsigned, or signature count mismatch                                                                                                                                                |
| Tip below minimum         | No qualifying tip instruction, or below the minimum; see [Tips](/falcon/tips)                                                                                                                              |
| Forward failed            | Falcon accepted it but could not forward it; only ever visible as a signature that never lands                                                                                                             |

There's no rate-limit feedback on UDP either. Pace your sends to your allowance.

Every reason above except *forward failed* produces an explicit, readable error on the JSON-RPC route, so when UDP submissions vanish, replay one identical transaction to `http://<region>.falcon.wtf` and read the real rejection.

## Retrying without a response

Keep the first signature of the transaction you sent. Poll it on a normal Solana RPC, and while it hasn't landed and the blockhash is still valid, resend the **same signed bytes**. Stop when it lands, or when the blockhash expires.

Don't re-sign to retry. A new signature is a distinct transaction, and it can land on its own.

Falcon does keep a signature-keyed cache of recent submissions and suppresses duplicates, but only once that signature has already been delivered, which is exactly why a landing-retry of the same bytes is still the right move. Delivery isn't exactly-once in either direction.

## Related

* [Falcon errors & retries](/falcon/errors)
* [Falcon regions & endpoints](/falcon/regions)
* [Telemetry](/falcon/telemetry)
