Railway API reliability
How the Railway API behaves under failure: the error convention and code catalog, rate limits and headers, retry/idempotency guidance, and the versioning/deprecation policy.
What it says
The document its publisher serves
the skill
Read from the publisher's own address, not re-hosted.
# Railway API reliability How the Railway API behaves under failure — errors, rate limits, retries, and deprecation — in one place, so an agent can depend on it without guessing. The public API is GraphQL at a single endpoint, `https://backboard.railway.com/graphql/v2`. Authentication is covered in <https://railway.com/auth.md>; the endpoint and OAuth surface are described in <https://railway.com/openapi.json>. ## Errors Railway follows the GraphQL error convention, not HTTP status alone. Always inspect the `errors` array, not just the status code. - **HTTP 200 with an `errors` array** — execution and authorization failures. A query that runs but is denied returns 200; the failure is in `errors`. - **HTTP 400** — the request could not be parsed or validated (malformed JSON, unknown field, bad variable). `extensions.code` carries the reason. - **HTTP 429** — rate limited. See below. Every error object carries a `message`, an `extensions.code`, and a `traceId`. Include the `traceId` when reporting a problem — it is how support finds the request. ```json { "errors": [ { "message": "Cannot query field \"nope\" on type \"Query\".", "extensions": { "code": "GRAPHQL_VALIDATION_FAILED", "traceId": "7992771584715554281" } } ], "data": null } ``` Common `extensions.code` values: | Code | Meaning | HTTP | | --- | --- | --- | | `GRAPHQL_PARSE_FAILED` | The query is not valid GraphQL syntax | 400 | | `GRAPHQL_VALIDATION_FAILED` | The query references unknown fields or types | 400 | | `BAD_USER_INPUT` | A field or variable failed validation | 400 | | `INTERNAL_SERVER_ERROR` | An unexpected error, or an authorization denial (message `Not Authorized`) | 200 | ## Rate limiting The API is metered per hour, per account or workspace token. The base budget is **1,000 requests per hour** (60-minute window). Some plans raise this; the response headers below always report the limit that applies to your token. Authenticated responses carry standard rate-limit headers so you can self-throttle: | Header | Meaning | | --- | --- | | `X-RateLimit-Limit` | Requests allowed in the window | | `X-RateLimit-Remaining` | Requests left in the current window | | `X-RateLimit-Reset` | ISO-8601 time the window resets | | `RateLimit-Policy` | The limit and window, advertised on every response (even unauthenticated) | When you exceed the limit the API returns **HTTP 429** with a `Retry-After` header (seconds). Back off until then; do not retry immediately. ## Retries - **Reads (queries) are safe to retry.** They have no side effects. - **Writes (mutations):** retry only on a network error or a 429/5xx where you did not receive a response. If you received a 200, the mutation ran — retrying may duplicate it. Most Railway mutations are keyed on the resource they act on, so a retry with the same arguments generally converges, but do not assume exactly-once. - **Back off exponentially** and honor `Retry-After` on a 429. - Carry the `traceId` from a failed response into any support request. ## Versioning and deprecation Railway's public API is GraphQL, so **the schema is the contract**. New fields and types are added without a version bump — additive changes never break an existing query. Fields being retired are marked `@deprecated` in the schema with a reason. Introspect with `includeDeprecated: true` to see them: ```graphql { __type(name: "Project") { fields(includeDeprecated: true) { name isDeprecated deprecationReason } } } ``` Prefer non-deprecated fields; a deprecated field keeps working through its sunset window and is removed only after it has been marked for a meaningful period. ## Status and incidents - **Status page:** <https://status.railway.com> — current platform status and incident history. - **Webhooks:** react to deployment and service events instead of polling. Delivery is best-effort — each event is POSTed with a 30-second timeout and retried up to 3 times with exponential backoff, treated as delivered on any 2xx/3xx — and carries no cryptographic signature, so authenticate the sender with a secret embedded in the webhook URL and reconcile against the API rather than treating a webhook as a guaranteed ledger. See <https://docs.railway.com/observability/webhooks>. ## Related - Authentication: <https://railway.com/auth.md> - OpenAPI description: <https://railway.com/openapi.json> - API documentation: <https://docs.railway.com/integrations/api> - Status: <https://status.railway.com> ## Open this page <https://railway.com/api-reliability.md>
What this is
Document
text/markdownlast seen 2026-08-30
These are the publisher's own words, read from what they serve at their own address.
Where it lives
The publisher's own address
https://railway.com/api-reliability.md
This catalog links to it and never serves a copy, so what you get is whatever railway.com is serving now.
In its own words
What its publisher says you would ask it
- how does the Railway API report errors
- is the Railway API safe to retry
- what are the Railway API rate limits
Tags
How its publisher filed it
docsapireliability