Concepts
Errors & billing
Authentication, authorization and billing are three separate outcomes here, and nothing that fails to return usable data costs anything. Both of those are specified behaviour with tests behind them, not a courtesy.
The order things happen in
Every request — synchronous, batch item, async job, or subscription poll — runs through the same eight stages in the same order. The order is what makes the guarantees hold: charging sits after the cache and before the run, and settlement sits after it.
- 01
validatefreeParameters are checked against the endpoint schema. A failure lists every bad path and never reaches a scraper.
- 02
preflightfreeMissing credentials or proxies fail here, before the cache, so a misconfigured deployment fails identically whether or not something happens to be cached.
- 03
cachefreeA hit inside your cache_max_age returns immediately with creditsCharged 0. A cache read failure degrades to a live fetch, never to an error.
- 04
chargemay chargeTaken only after a miss, only when the endpoint costs more than zero, and only if the balance covers it. A balance can never go negative.
- 05
runfreeThe scraper executes. Async jobs run here too, with no request timeout above them.
- 06
settlefreeAnything that threw is refunded in full. Anything empty is refunded in full. An endpoint may charge less than its listed price, never more.
- 07
cache writefreeThe result is stored for the next caller, unless your team has cache writes or retention turned off.
- 08
logfreeExactly one request log row per request, success or failure. A failed log write never fails the request.
Every error code
The charged column reads no on every row. That is the point of the table. The origin column is the one to act on: a you error needs a change on your side, a platform error usually needs a retry, and a us error needs nothing from you at all.
| Code | HTTP | Origin | Retry | Charged | Meaning |
|---|---|---|---|---|---|
| missing_api_key | 401 | you | no | no | No x-api-key header was sent. |
| invalid_api_key | 401 | you | no | no | The key is not recognised. This is a typo, not a billing problem. |
| revoked_api_key | 401 | you | no | no | The key existed and was revoked. Issue a new one. |
| account_suspended | 403 | you | no | no | The account has been suspended. Contact support to restore access. |
| insufficient_credits | 402 | you | no | no | The key is valid and the balance is not. The only condition that returns 402. |
| daily_cap_exceeded | 429 | you | no | no | Your configured daily cap would be exceeded. Checked before any charge. |
| invalid_request | 400 | you | no | no | Parameters failed validation. Every failing path is listed, not just the first. |
| not_found | 404 | you | no | no | The route or resource does not exist on this API. Check the path. |
| upstream_not_found | 404 | you | no | no | The profile, video, or post does not exist or is not public. |
| empty_result | 200 | platform | no | no | The target exists and has nothing to return. A success, and free. |
| upstream_blocked | 502 | platform | yes | no | The platform refused the request. Ours to fix, not yours. |
| upstream_rate_limited | 429 | platform | yes | no | The platform throttled us. Back off and retry. |
| upstream_unavailable | 503 | platform | yes | no | The platform is unreachable. |
| upstream_timeout | 504 | platform | yes | no | The upstream request ran out of time. Use a job for work this long. |
| upstream_schema_drift | 502 | platform | no | no | The platform changed its response shape. We are alerted automatically. |
| not_configured | 501 | us | no | no | This deployment is missing credentials or proxies for that endpoint. The response names them. |
| too_many_targets | 400 | you | no | no | A batch exceeded the maximum target count. |
| job_not_found | 404 | you | no | no | No job with that id belongs to your team. |
| internal_error | 500 | us | no | no | Our fault. Refunded, logged, and alerted on. |
401 is not 402
A missing, unrecognised, or revoked key always returns 401. A 402 means the key is valid and the balance is not. Nothing else returns 402, so a 402 in your logs is never something you fix by checking for a typo.
This matters more than it sounds. A competitor returns 402 out of credits for an invalid key, which sends people to buy credits when what they actually have is a stray whitespace character in an environment variable.
What an error looks like
{
"success": false,
"error": {
"code": "upstream_blocked",
"message": "The platform blocked this request. It was not charged.",
"details": { }
}
}Empty is a success
A channel with no uploads, a video with no captions, a search with no hits — the target exists and the answer is nothing. You get a 200, an empty payload, and creditsCharged: 0. Treat empty as data, not as failure.
{
"success": true,
"data": { "items": [] },
"meta": {
"endpoint": "youtube.channel-videos",
"creditsCharged": 0,
"cached": false,
"durationMs": 1104,
"requestId": "req_01JB8Z..."
}
}The clauses this page implements
| P1 | You are never charged for a failed request. | SPEC §2.5 |
| P2 | You are never charged for an empty result. | SPEC §2.5 |
| P3 | Cache hits cost 0 credits. | SPEC §2.3 |
| P4 | An invalid API key is an auth error, not a billing error. | SPEC §3 |
| P5 | Every endpoint's price is machine-readable. | SPEC §2.6 |
| P6 | A balance can never go negative. | SPEC §1.1 |
| P7 | Per-tenant cache isolation is standard. | SPEC §2.3 |
| P8 | Hard daily spend caps. | SPEC §1.3 |