Authentication
Datorus uses HTTP Basic auth over the proxy gateway. Send your gateway username and password with every request; route the request through the gateway with your HTTP client's proxy option. New here? Start with the price-monitoring quickstart.
curl -x http://USERNAME:PASSWORD@gw.datorus.io:8001 \
-L https://target.example.com/productsEndpoints
GETgw.datorus.io:8001 is the proxy gateway —
point any HTTP client's proxy setting at it. Datorus relays your request to the
destination and returns the upstream response unchanged. There is no Datorus REST
API surface for the proxy itself; you talk to your target through the gateway.
Sessions
By default each connection gets a fresh upstream session. To reuse one upstream session across a multi-step flow, send a session id header.
Live: send X-Datorus-Session-Id — an opaque id you generate (allowed
characters A–Z a–z 0–9 _ -, up to 128 chars) — to pin a multi-step flow to a
stable upstream egress. The gateway honours the header, echoes the same id back on
the response, and scopes the affinity to your account, so your id can never collide
with another customer's session. An invalid or absent header falls back to the
default per-connection session (it is never an error). The exact affinity window
(TTL), rotation, and concurrency limits are being finalized in Phase 7 load testing
— subscribe to the changelog for the published values.
Rate limits
Live: the gateway emits X-Datorus-RateLimit-Limit, X-Datorus-RateLimit-Remaining,
and X-Datorus-RateLimit-Reset on every response, and a 429 includes a Retry-After
header per RFC 7231. The per-credential limit is a soft limit while we finish Phase 7
load testing; the headers always reflect the limit currently in force. The final
concrete numbers (requests/second, burst) ship after load testing — subscribe to the
changelog for the published values.
Error responses
Every error uses one canonical JSON envelope. The machine-readable code is
SCREAMING_SNAKE_CASE and is never translated; message is English-only;
requestId is a UUID; timestamp is ISO 8601 UTC.
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Your balance is too low to complete this request.",
"details": { "currentBalanceCents": 0, "requiredCents": 120 },
"requestId": "0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01",
"timestamp": "2026-05-19T14:32:18Z"
}
}
The Example response column is the complete error envelope for each code
(code + message + details + requestId + timestamp); requestId and
timestamp vary per request. message is English-only regardless of locale.
| Code | HTTP | When it happens | Example response |
|---|---|---|---|
VALIDATION_ERROR | 400 | The request was malformed or a field failed validation. | {"error":{"code":"VALIDATION_ERROR","message":"The submitted data is invalid.","details":{"field":"step","reason":"must be 4 or 5"},"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
TOKEN_INVALID | 400 | A signup/verification token could not be parsed. | {"error":{"code":"TOKEN_INVALID","message":"The token is invalid.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
TOKEN_EXPIRED | 410 | A signup/verification token has expired. | {"error":{"code":"TOKEN_EXPIRED","message":"The token has expired.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
TOKEN_USED | 409 | A signup/verification token was already consumed. | {"error":{"code":"TOKEN_USED","message":"The token was already used.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
INSUFFICIENT_BALANCE | 402 | A paid account's balance is too low for the request. | {"error":{"code":"INSUFFICIENT_BALANCE","message":"Your balance is too low to complete this request.","details":{"currentBalanceCents":0,"requiredCents":120},"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
TRIAL_EXHAUSTED | 402 | The free-trial allocation is used up — top up to continue. | {"error":{"code":"TRIAL_EXHAUSTED","message":"Your free trial is used up.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
STRIPE_DECLINED | 402 | The card was declined during a top-up. | {"error":{"code":"STRIPE_DECLINED","message":"The card was declined.","details":{"declineCode":"card_declined"},"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
STRIPE_3DS_REQUIRED | 402 | The card requires 3-D Secure authentication. | {"error":{"code":"STRIPE_3DS_REQUIRED","message":"Card authentication is required.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
AUP_VIOLATION | 403 | The request violates the Acceptable Use Policy. | {"error":{"code":"AUP_VIOLATION","message":"This request violates the Acceptable Use Policy.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
ACCOUNT_SUSPENDED | 403 | The account is suspended. | {"error":{"code":"ACCOUNT_SUSPENDED","message":"This account is suspended.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
RESOURCE_NOT_FOUND | 404 | The requested resource does not exist. | {"error":{"code":"RESOURCE_NOT_FOUND","message":"The requested resource was not found.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
INVALID_CREDENTIAL | 407 | The gateway rejected your proxy username or password. | {"error":{"code":"INVALID_CREDENTIAL","message":"Invalid proxy credentials.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
EMAIL_TAKEN | 409 | An account already exists for this email. | {"error":{"code":"EMAIL_TAKEN","message":"An account with this email already exists.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
EMAIL_PROVIDER_MISMATCH | 409 | This email is registered with a social-login provider. | {"error":{"code":"EMAIL_PROVIDER_MISMATCH","message":"This email uses a social-login provider.","details":{"provider":"google"},"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
RATE_LIMITED | 429 | Too many requests — back off and retry after Retry-After. | {"error":{"code":"RATE_LIMITED","message":"Too many requests.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
UPSTREAM_FAILED | 502 | The destination returned an invalid or unreachable response. | {"error":{"code":"UPSTREAM_FAILED","message":"The upstream destination failed.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
SERVICE_UNAVAILABLE | 503 | The gateway is temporarily unavailable — retry shortly. | {"error":{"code":"SERVICE_UNAVAILABLE","message":"The service is temporarily unavailable.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
GATEWAY_TIMEOUT | 504 | The destination took too long to respond. | {"error":{"code":"GATEWAY_TIMEOUT","message":"The upstream destination timed out.","details":null,"requestId":"0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01","timestamp":"2026-05-19T14:32:18Z"}} |
Reserved headers
The Status column is honest about what the gateway sends today. These headers
ride the HTTP forward response and the CONNECT 200 Connection established (both are
proxy-layer headers the gateway controls); the inner tunneled HTTPS payload is
end-to-end encrypted, so the proxy cannot add headers there.
| Header | Direction | Status | Description | Example |
|---|---|---|---|---|
X-Datorus-Used-Bytes | response | live (on 402) | Bytes this request consumed against your trial/balance. | 421 |
Retry-After | response (429) | live | Seconds (or HTTP date) to wait before retrying, per RFC 7231. | 120 |
X-Datorus-Request-Id | response | live | Per-request id, 1:1 with the envelope requestId. | 0190f8c2-7a3b-4d8e-9f1c-2b5e8a4d6f01 |
X-Datorus-Session-Id | request + response | live | Client-generated id to activate a sticky session; echoed back. | 7f3a9b2c-1d4e-4a8f-b0c2-9e5d7a3f6b21 |
X-Datorus-RateLimit-Limit | response | live | Current rate limit (requests/second); the concrete value is being finalized in load testing. | (varies) |
X-Datorus-RateLimit-Remaining | response | live | Requests remaining in the window. | 7 |
X-Datorus-RateLimit-Reset | response | live | Unix timestamp when the window resets. | 1748345538 |
Idempotency
Proxy requests are not idempotent by nature — they are forwarded to your target as sent. Billing top-ups use Stripe's idempotency keys internally so a retried checkout never double-charges.
Webhooks
Datorus does not deliver outbound webhooks for proxy traffic. Billing events are handled internally via Stripe; you do not need to host a webhook endpoint.
OpenAPI
A machine-readable OpenAPI description is planned. Until then, this page is the reference — subscribe to the changelog for updates.