Handle API responses

ROIC API v3 uses consistent resource, list, and error shapes. Learn these shapes once, then use them across every endpoint. This guide also covers pagination, request limits, and retries.

Read a resource response

A successful single-resource request returns the resource directly. It isn't wrapped in a data field.

{
  "id": "tkr_cO46h8pgSu9Qqy",
  "object": "v3.reference.ticker",
  "symbol": "NASDAQ:AAPL",
  "ticker": "AAPL"
}

Every resource contains a stable public id, a constant object type, and resource-specific fields. Nullable fields remain present as null when a value is unavailable.

Read a list response

A successful list request returns a data array and URLs for moving through the collection.

{
  "data": [],
  "next_page_url": "/v3.0.0/tickers?limit=50&page=page_v1_opaque_token",
  "previous_page_url": null
}

The first page has previous_page_url: null. The final page has next_page_url: null.

Follow pagination URLs

Most list endpoints use cursor pagination. Request the first page without a page parameter, then follow the returned URL.

bash
curl "https://api.roic.ai/v3.0.0/tickers?limit=50" \
  -H "Authorization: Bearer $ROIC_API_KEY"

Bearer-authenticated clients can follow next_page_url unchanged while continuing to send the same Authorization header.

bash
curl "https://api.roic.ai/v3.0.0/tickers?limit=50&page=page_v1_opaque_token" \
  -H "Authorization: Bearer $ROIC_API_KEY"

Query-authenticated clients must append apikey again because returned page URLs never contain credentials. Page-size defaults and maximums vary by endpoint; each endpoint page documents its limit parameter.

Treat page tokens as opaque

Don't decode, edit, or create page tokens. Each token is bound to the endpoint path, filters, order, resource family, and limit that created it.

Start a new request without page after changing a filter, sort order, or page size. Tokens expire after one hour. Modified, expired, or mismatched tokens return 400 Bad Request in the standard error envelope.

The API uses stable keyset ordering, but data can change between requests. Store the returned resources if you need a reproducible dataset.

Recognize unpaginated lists

The metric-definition list returns every matching definition in one response. It doesn't accept limit, page, or order, and both page URLs are always null.

Read a binary response

GET /v3.0.0/company/logo/{identifier} is the only binary exception. It returns raw WebP or PNG bytes with Content-Type, ETag, and Cache-Control headers. A conditional request can return 304 Not Modified with an empty body.

Logo failures still return the standard JSON error envelope.

Handle API errors

Every failure returns an error object with stable fields your client can inspect.

{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_missing",
    "message": "The requested resource does not exist.",
    "request_id": "req_0af7651916cd43dd8448eb211c80319c"
  }
}

Branch on type and code, not the human-readable message. The optional param field identifies a request value that you can change.

Use HTTP status codes

HTTP status communicates the broad failure category. The error type and code provide stable programmatic detail.

StatusTypeMeaning
400invalid_request_errorA parameter, body, or page token is invalid.
401authentication_errorCredentials are missing, conflicting, malformed, or unknown.
402payment_required_errorThe authenticated caller's plan doesn't include access.
404invalid_request_errorA resource or v3 route doesn't exist.
409conflict_errorThe request conflicts with existing state.
429rate_limit_errorThe caller exceeded a request limit.
500api_errorAn unexpected server failure occurred.
503api_errorA required dependency is unavailable.

Handle rate limits

Use rate-limit headers to monitor your request quota and Retry-After to recover from 429 Too Many Requests responses.

Understand request limits

Each plan provides a request quota with a per-minute limit and an available history range. v2 and v3 draw from the same quota, so requests to either version reduce the same remaining count.

PlanRequests / minuteHistory
Free52 years
Individual3005 years
ProfessionalUnlimitedAll available
EnterpriseUnlimitedAll available

The API uses a sliding 60-second window per API key. An authenticated request counts before parameter validation, so invalid parameters still consume one request when the limit hasn't been reached. See pricing for current plan details.

Read rate-limit headers

Metered responses normally include three headers. Read them after each request instead of estimating usage in your client.

HeaderMeaning
X-RateLimit-LimitEnforced request limit for the current window.
X-RateLimit-RemainingRequests remaining after the current request.
X-RateLimit-ResetUnix timestamp when the oldest request leaves the window.

Treat these headers as optional because they can be absent from a response.

Recover from a request limit

When you reach the limit, the API returns 429 Too Many Requests, a Retry-After header measured in seconds, and the standard error envelope.

{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded for the free plan. Retry in 42 seconds or update your plan.",
    "request_id": "req_0af7651916cd43dd8448eb211c80319c"
  }
}

Stop sending requests for the duration of Retry-After. If another 429 follows, use capped exponential backoff with jitter. If normal traffic regularly reaches the limit, choose a plan with a larger request quota.

Trace a request

Every success and failure includes a Request-Id response header. Error bodies repeat the same value in error.request_id. Include this ID when you contact support so the request can be matched to server traces.

Unexpected failures are redacted. Responses don't expose stack traces, database messages, credentials, or dependency payloads.

Retry failed requests

Choose a retry policy from the status and error code. Error responses use Cache-Control: no-store and must not be cached.

  • For 429, wait for Retry-After as described in recover from a request limit.
  • For transient 500 and 503 responses, use capped exponential backoff with jitter.
  • For validation or authentication errors, change the request before retrying.

Next steps

Apply these response rules to the resource family you want to use.