Authentication
API keys, scopes and the request envelope.
TextToQuant exposes a small REST API you can call from any language, plus an
MCP server for AI agents. The REST API and the downloadable MCP server both
authenticate with the same API key and share your billing and plan limits. The hosted MCP
endpoint is different: it uses OAuth (scopes mcp:read / mcp:run / mcp:write), not an API key
see the MCP page for that flow.
API keys#
Create a key under Account → API keys. It's shown once, so store it safely. You can have up to 5 active keys, and revoking one cuts off access instantly.
Send the key on every request, as either header:
Authorization: Bearer ttq_your_key
# or
x-api-key: ttq_your_keyProgrammatic access requires a plan with API access. Keys are shown once at creation. Store them securely.
The response envelope#
Every response, success or failure, shares one shape. A 2xx reply is the mirror of an error:
// success
{ "success": true, "data": { /* … */ } }
// list endpoints also add pagination
{ "success": true, "data": { "results": [ /* … */ ] }, "pagination": { "limit": 10, "offset": 0, "total": 1, "hasMore": false } }
// error
{ "success": false, "code": "invalid_api_key", "error": "Invalid or revoked API key. Create a new one at Account → API keys." }The code is stable and safe to switch on; the human readable error text may be reworded. Error
responses also include a requestId. Quote it when contacting support.
Rate limits#
120 requests/minute per IP and 240/minute per account, on top of your plan quotas. Every
response carries standard RateLimit headers (limit, remaining, reset). Pace yourself with them.
Over a limit you get a 429 with code rate_limited.
Safe retries (idempotency)#
Send an Idempotency-Key header (or idempotency_key in the body) on POST /v1/backtests. Replaying
the same key with the same body within 24h returns the original run, no second credit. The same
key with a different body returns 409 idempotency_key_reuse.
Error reference#
| HTTP | code | What it means |
|---|---|---|
| 401 | missing_api_key | No key on the request: add the header |
| 401 | invalid_api_key | Key is wrong, revoked, or malformed |
| 403 | feature_not_available | Your plan doesn't include this feature (requiredFeature says which) |
| 403 | plan_history_limit / plan_timeframe_limit | Date range or timeframe beyond your plan |
| 400 | invalid_query / validation_error | Bad input: the message names what failed. Nothing billed |
| 422 | clarification_needed | Parse hit ambiguous terms: data.clarificationsNeeded lists them |
| 422 | missing_parameters | Parse missing a required field: details.missingFields + suggestions |
| 422 | invalid_strategy | Parsed strategy isn't executable: details.structuralErrors say why |
| 422 | cannot_execute | Pre run validation failed: the message lists failing checks |
| 409 | idempotency_key_reuse | Same key, different body: use a fresh key |
| 413 | WORKLOAD_TOO_LARGE | Backtest exceeds the bar count ceiling |
| 429 | rate_limited | Over the request rate: pause and retry |
| 429 | OVERAGE_* | Billing state gate (pending / overdue / cap reached) |
| 404 | not_found | Unknown endpoint, or an id you don't own |
| 503 | QUEUE_BACKPRESSURE / queue_unavailable | Queue busy or down: retryable, credit refunded |
| 500 | execution_failed | The run failed: nothing saved, metered credit refunded |
| 500 | parse_failed | Parser couldn't read the query: details.suggestions hint a fix |
| 500 | internal_error | Unexpected server error: safe to retry |
Usually not. Backtests finish server side even if the HTTP connection drops, and the credit was
already counted. Wait a moment, then call GET /v1/backtests. The newest row is your run.
Next: the endpoint reference.