On this page
- At a glance
- Responses from the gateway
- Gateways with the newer response format
- api_key_missing
- api_key_invalid
- access_denied
- subscription_required
- subscription_inactive
- subscription_not_started
- subscription_expired
- quota_exceeded
- rate_limit_exceeded
- route_not_found
- service_unavailable
- upstream_error
- Retries with backoff and jitter
- When you need support
- See also
At a glance#
The gateway answers its own refusals with a JSON body that holds a message field. Every response, the gateway's and the provider's, carries an X-Kong-Request-Id header. An error raised by the provider's own service is passed through unchanged, with the provider's status and body.
{
"message": "No active subscription for this product"
}A call the gateway cannot match to a subscription gets 403, not 401, unless the host runs the newer format described below. That includes a call with no key, with an unknown key, with the key of the other environment and with a key in a header of another name. The gateway does not tell these cases apart, so the message is the same.
Responses from the gateway#
| Status | message | What it means and what to do |
|---|---|---|
403 | No active subscription for this product | The gateway could not match your key to an active subscription of this product. Check the host, the header name, the key and its environment, then that the product is added to the app. See the table below. |
403 | Subscription is not active | The gateway knows the subscription but it is not active, for example it expired, or a renewal waits for payment or approval. Open My Subscriptions. |
403 | Subscription quota exceeded | The quota of your subscription is spent. Do not retry. |
429 | Endpoint quota exceeded | The quota of this endpoint is spent. Do not retry. |
403 | No endpoint quota rule found for this consumer | The product is priced per endpoint and your subscription has no quota for this endpoint. Check the endpoints of your subscription. |
403 | Authentication required, Consumer not identified | The gateway could not identify the app. Send the key in the api360key header. |
404 | no Route matched with those values | No route matches. Check the gateway address, the product slug, /v and the version, the path and the method. The body also carries a request_id. |
503 | Subscription service unavailable, Endpoint quota service unavailable | A part of the gateway is temporarily unavailable. Retry with backoff. |
500 | Route configuration error | The route is misconfigured on the platform's side. Contact support and quote the request ID. |
400 | Endpoint quota rule misconfigured | The quota rule of the endpoint is wrong on the platform's side. Contact support and quote the request ID. |
When you read the 403 with No active subscription for this product, find your mistake in this table:
| You did | What to change |
|---|---|
Sent no key, or put it in another header such as Authorization or x-api-key | Send api360key: <key>. The key is also accepted as the query parameter api360key, but a URL is written to logs, so prefer the header. |
| Used a key of a Testing app on the Production gateway, or the reverse | Use the key of an app of the environment of the gateway (Sandbox and Production). |
| Deleted the key | Use a key that still exists on the app (apps and keys). |
| Did not add the product to the app, or did not subscribe | Subscribe, then add the product to the app (subscriptions). |
| Called a few seconds after you made the change | Wait a minute and send the request again. |
# a key the gateway does not know: the same answer as no key at all curl -sS "$API360_BASE_URL$API360_PATH" -H "api360key: not-a-real-key"
# a key sent in another header is not read curl -sS -o /dev/null -w "%{http_code}\n" "$API360_BASE_URL$API360_PATH" -H "x-api-key: not-a-real-key"
# no product behind this path curl -sS "$API360_GATE_URL/no-such-product/v1/x"
Gateways with the newer response format#
Some gateway hosts already answer in a newer format. A response with this format has a JSON object error and keeps the same status codes. Match on code: the message is in Arabic or English according to the Accept-Language header of your request.
{
"error": {
"code": "api_key_missing",
"message": "No API key was provided. Send your key in the api360key header.",
"request_id": "03284e42d54f8e25b7cce16b65f7a86a",
"docs_url": "https://api360.sa/docs/consumer/errors-and-retries#api_key_missing"
}
}On such a host a call with no key is a 401 and the missing or invalid key has its own code. The codes you meet most often follow. The docs_url of a response points to its code below.
api_key_missing#
401. The request carried no key. Send it in the api360key header.
api_key_invalid#
401. The gateway does not know the key or the credentials: it is wrong, deleted, or from the other environment. Check the key against the app and the host.
access_denied#
403. The key is valid, but the app is not allowed on this product. Add the product to the app.
subscription_required#
403. The key is valid and there is no active subscription for this product. Subscribe to it.
subscription_inactive#
403. The gateway knows the subscription but it is not active, for example it expired, or a renewal waits for payment or approval.
subscription_not_started#
403. The subscription starts later. Try again after its start date.
subscription_expired#
403. The subscription expired. Renew it.
quota_exceeded#
429. Your quota is spent. Retry only after the time in Retry-After, or change your subscription.
rate_limit_exceeded#
429. Too many requests. Retry after the time in Retry-After.
route_not_found#
404. No API matches the host, the path and the method.
service_unavailable#
503. The service is temporarily unavailable. Retry with backoff, after the time in Retry-After.
upstream_error#
502. The provider's service answered with an invalid response or could not be reached. Retry with backoff.
Other codes exist for the same purposes; their message says what to do. A 500 with a request ID in the body means contact support.
Retries with backoff and jitter#
Retry only temporary failures: a network error, and the answers 502, 503 and 504. Retry at most three times, and wait a random interval that grows each time, so that all your clients do not retry at the same instant. Never retry a refusal for a quota or a subscription (403 and 429 as above), a 404, or another 4xx: waiting does not change the answer.
const RETRY = new Set([502, 503, 504]); const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); // call(fn): fn sends the request and returns a Response; the result is the first answer that is not worth a retry async function call(fn, { tries = 3, baseMs = 1000 } = {}) { for (let i = 0; ; i++) { let res; try { res = await fn(); } catch (err) { if (i >= tries - 1) throw err; // a network error: try again, then give up await sleep(2 ** i * baseMs + Math.random() * 0.4 * baseMs); continue; } if (!RETRY.has(res.status) || i >= tries - 1) return res; await sleep(2 ** i * baseMs + Math.random() * 0.4 * baseMs); } } // demo with a fake service that answers 503 twice and then 200 let sent = 0; const res = await call(async () => new Response("{}", { status: ++sent < 3 ? 503 : 200 }), { baseMs: 1 }); console.log(res.status === 200 ? `ok:${sent}` : `failed:${res.status}`);
A request that changes data is not safe to retry after a timeout: the first attempt may have succeeded, and a second one then runs the work twice. Retry such a request only when the product says it is safe.
When you need support#
Send the X-Kong-Request-Id value of the response, the time of the call, the product and the environment (get help). On a gateway with the newer format, send request_id from the body as well.
The newer response format on every gateway host, Retry-After on throttling answers, and an Idempotency-Key header that makes a retry of a POST request safe.