Skip to content

Region routing

Photon relays from multiple geographic regions. Each active region has its own relayer identity and its own RPC connectivity, so a transaction submitted from Europe can be signed and broadcast from a European region while a submission from the US goes out through a US one — shorter network paths between you, the relayer, and the chain's RPC endpoints mean lower submission latency. Every request that creates a transaction (Submit a transaction, Submit and wait, Submit a signed transaction) is resolved to exactly one region before it is queued, and that region relays it end to end.

How a region is resolved

Signals are checked in order; the first match wins:

  1. X-Edith-Region header — an explicit region name on the request.
  2. region body field — the same, in the request body. Top-level on submit and submit-signed requests; nested as tx.region on submit-and-wait.
  3. Country at the edge — the ISO-3166 alpha-2 country code observed by the CDN edge in front of the API, mapped to the region serving that country.
  4. Geo-IP — a geographic lookup of your client IP, mapped the same way.
  5. Default region — when nothing else matches.

Two properties worth knowing:

  • Only active regions are ever chosen. An explicit region name that isn't active — including a typo — is silently skipped, and resolution falls through to the next signal. A wrong region name never fails a request.
  • Country signals can't be forged. The country header is only honoured when it was set by the platform's own edge network; a caller supplying it directly is ignored, and their IP is geo-located instead. Explicitly pinning a region with X-Edith-Region is always allowed — it isn't a trust decision, and your per-key rate limits apply regardless of region.

The submitRegion field

Every transaction record carries submitRegion — the region that relayed it. It's returned by Get transaction status and in the Submit and wait response, so you can always see where a given transaction went out from.

One relayer identity per region

Each region signs with its own relayer key, so the on-chain from address of a sponsored transaction differs by region. Two consequences:

  • Don't hardcode a relayer address in contract allowlists or off-chain matching logic — resolve the sender from the receipt, or read submitRegion from the record.
  • If you need a stable sender address, use Authenticated transactions: a key derived for your derivationPath gives you the same from address on every submission, in every region.

Usage

Pin a region explicitly with the header (region names like us-east or eu-west are examples — an inactive name simply falls through). Requests are authenticated with your api-key header — see Get API access.

curl -X POST https://photon.example.com/api/submit-tx-sync \
  -H "Content-Type: application/json" \
  -H "api-key: YOUR_API_KEY" \
  -H "X-Edith-Region: eu-west" \
  -d '{
    "tx": {
      "to": "0x3dbE34f2C21b3B2980d4dc53f3c7E51e39663F49",
      "callData": "0xa9059cbb...",
      "chainId": 8453
    },
    "timeoutMs": 30000
  }'
const res = await fetch("https://photon.example.com/api/submit-tx-sync", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "api-key": process.env.EDITH_API_KEY!,
    "X-Edith-Region": "eu-west",
  },
  body: JSON.stringify({
    tx: {
      to: "0x3dbE34f2C21b3B2980d4dc53f3c7E51e39663F49",
      callData: "0xa9059cbb...",
      chainId: 8453,
    },
    timeoutMs: 30000,
  }),
});
const { data } = await res.json();
console.log(data.submitRegion); // "eu-west"

The response record shows which region relayed the transaction:

{
  "success": true,
  "data": {
    "id": "68fa3450539a3c9d28bbca33",
    "userId": "68c275846a6ba1c9a2198a8c",
    "to": "0x3dbE34f2C21b3B2980d4dc53f3c7E51e39663F49",
    "callData": "0xa9059cbb...",
    "value": "0",
    "chainId": 8453,
    "gasPrice": null,
    "maxFeePerGas": null,
    "maxPriorityFeePerGas": null,
    "gasLimit": null,
    "label": null,
    "status": "EXECUTED",
    "executionTxHash": "0x7d1a...c4e9",
    "timestamp": "2026-07-29T09:12:44.120Z",
    "latency": 2380,
    "costUSD": 0.42,
    "retries": 0,
    "isAccountCharged": true,
    "transactionType": "flash",
    "submitRegion": "eu-west"
  },
  "timestamp": "2026-07-29T09:12:46.500Z"
}

Info

Most integrations should send no region signal at all — automatic resolution already routes each request to the nearest active region. Pin a region only when you have a specific reason, such as keeping all of a workflow's transactions on one relayer identity.

Next