Errors
Structured error envelope and the full code registry. Plus the difference between 402 insufficient_credits and 429 rate_limited.
Error envelope
Every v2 error response shares the same shape — an error object with a stable code, a human-readable message, optional structured details, and a request_id for support escalation.
{
"error": {
"code": "validation_error",
"message": "Field 'question' must be a non-empty string.",
"details": {
"field": "question",
"received": ""
},
"request_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
}code— Stable, machine-readable string. Switch on this in your error handler — never the message.message— Human-readable description. Safe to log; not safe to surface to end-users without translation.details— Optional structured payload. Shape varies by code — for validation_error it's the offending field and value.request_id— Unique per request. Quote this in any support ticket — we can find the trace from this alone.
Code registry
Every code we emit, what it means, and the HTTP status it travels on.
| HTTP | Code | Meaning |
|---|---|---|
400 | invalid_request | The request was malformed (bad JSON, missing required field, wrong content-type). |
401 | unauthenticated | Your API key is missing, malformed, or revoked. |
402 | insufficient_credits | Your tenant ran out of credits. Top up before retrying. |
403 | forbidden | Your key is valid but lacks permission for this resource (often a tenant mismatch). |
404 | job_not_found | No job exists with that ID — or it belongs to another tenant and you can't see it. |
404 | resource_not_found | Generic not-found for non-job resources (apps, transactions, etc.). |
409 | conflict | Idempotency key collision, body-hash mismatch, or in-flight key reuse. See the idempotency guide. |
422 | validation_error | Request shape is fine but a field value is invalid (e.g. effort='warp_speed'). |
429 | rate_limited | Too many requests in the rate-limit window. Back off and retry. |
500 | internal_error | Server-side bug. Quote the request_id in a support ticket. |
503 | upstream_unavailable | An upstream service (data provider, MCP tool) is degraded. Usually transient — retry. |
402 insufficient_credits vs 429 rate_limited
Both prevent your request from succeeding, but they mean different things and require different handling. Don't conflate them.
402 insufficient_credits402 — out of credits
Your tenant has zero credits remaining. Backing off and retrying won't help — top up the account or contact your admin. The X-Grep-Credits-Remaining header will be 0.
429 rate_limited429 — too many requests
You're sending requests faster than your rate limit allows. Back off using the Retry-After header and retry. Credits aren't depleted — this is purely about request frequency.
Pro tip: switch on code, not status
Two different error codes can travel on the same HTTP status (e.g. 404 covers both job_not_found and resource_not_found). Always branch on error.code in your client — it's the stable contract.