Skip to main content
What you’ll learn
  • How every TestDino API endpoint authenticates, paginates, and reports errors
  • When to use predefined date ranges vs custom startDate / endDate
  • How ?include= lets you bundle related data into a single request
Every endpoint in the TestDino Public API follows the same conventions for authentication, response shape, errors, and pagination.

Quick reference

TopicSummary
AuthenticationBearer token (tdp_ prefix) with public-api scope
Rate limits100/min per token · 200/min per IP · 1/min for PDF
Response format{ success, data, pagination? } or { success, error }
ErrorsUniform error codes with human-readable messages
Paginationpage + limit query params, pagination in response
Date filteringdateRange (presets) or startDate + endDate (custom)
Include parameters?include= adds related data to the response

Authentication

All endpoints require a Bearer token in the Authorization header:
Authorization: Bearer tdp_your_token_here
RequirementValue
Token typeProject PAT (scoped to a single project)
Token prefixtdp_
Required scopepublic-api
Header formatAuthorization: Bearer tdp_...
StorageEnvironment variable, secret manager — never commit to source
Generate a Project PAT with the public-api scope via Generate API keys to access TestDino’s public-facing APIs. The PAT’s project must match the {projectId} in every request URL, otherwise you’ll get a 403.
NoteTokens can be rotated or revoked from Project Settings → API Keys at any time. A revoked token stops working immediately and returns 401 AUTH_INVALID_TOKEN.

Rate limits

ScopeLimitWindow
Per token100 requests1 minute
Per IP (pre-auth)200 requests1 minute
PDF generation1 request1 minute (per token)
Every response includes standard rate-limit headers:
HeaderMeaning
RateLimit-LimitMax requests allowed in the current window
RateLimit-RemainingRequests remaining in the window
RateLimit-ResetSeconds until the window resets
When you exceed a limit, the API returns 429 RATE_LIMIT_EXCEEDED. PDF-specific 429 responses also include a Retry-After header indicating how many seconds to wait.
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests"
  }
}
TipFor batch work, back off to 1 request/second (60/min) to leave headroom. If you need more, throttle on RateLimit-Remaining and pause when it drops below 10.

Response format

Every successful response uses this envelope:
{
  "success": true,
  "data": { /* endpoint-specific payload */ },
  "pagination": { /* present on list endpoints only */ }
}
Every error response uses this envelope:
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description"
  }
}
FieldTypeDescription
successbooleantrue for 2xx responses, false otherwise
dataobject or arrayEndpoint payload (omitted on error)
paginationobjectPresent on list endpoints — see Pagination
error.codestringStable machine-readable code — safe to branch on
error.messagestringHuman-readable description — safe to log, not safe to parse

Errors

Branch your client on error.code, not HTTP status alone. Codes are stable; messages may change.
HTTPError codeWhen it happensAction
400VALIDATION_ERRORMissing or invalid query/path parameterFix the request, don’t retry
401AUTH_INVALID_TOKENToken missing, malformed, expired, or revokedRe-issue the token, fix header
403FORBIDDENValid token but wrong project or missing scopeUse the PAT that matches projectId
404NOT_FOUNDResource doesn’t exist or isn’t accessible to this tokenVerify the ID, check project scope
429RATE_LIMIT_EXCEEDEDToo many requests in the windowBack off using RateLimit-Reset / Retry-After
5xxINTERNAL_ERRORServer-side failureRetry with exponential backoff
TipRetry only on 429 and 5xx. Use exponential backoff starting at 1 second, capped at 30 seconds, with jitter. Never retry 400, 401, 403, or 404.

Pagination

List endpoints accept page and limit:
ParameterTypeDefaultRange
pageinteger1≥ 1
limitinteger201–100
Pagination metadata is included in the response:
{
  "success": true,
  "data": [ /* items for this page */ ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 134,
    "hasNext": true,
    "hasPrev": false
  }
}
Iterate all pages in Node.js:
let page = 1;
const all = [];
while (true) {
  const res = await fetch(
    `${BASE_URL}/${projectId}/test-runs?page=${page}&limit=100`,
    { headers: { Authorization: `Bearer ${token}` } }
  );
  const body = await res.json();
  all.push(...body.data);
  if (!body.pagination.hasNext) break;
  page += 1;
}
NoteA few endpoints use offset + limit instead of page + limit (e.g. GET /test-cases/history). The parameter name is documented on each endpoint’s reference page.

Date filtering

Endpoints that filter by time accept either a predefined range or a custom range. Predefined ranges — pass dateRange:
ValueMeaning
7dLast 7 days
14dLast 14 days
30dLast 30 days
60dLast 60 days
90dLast 90 days
GET /{projectId}/dashboard?dateRange=30d
Custom ranges — pass startDate + endDate (ISO 8601 dates):
GET /{projectId}/dashboard?startDate=2026-03-01&endDate=2026-03-31

Precedence

If both dateRange and startDate/endDate are provided, dateRange takes precedence and the custom dates are ignored. Pick one:
If you want…SendDon’t send
Preset rangedateRange=30dstartDate, endDate
Custom rangestartDate=..., endDate=...dateRange
NotestartDate and endDate must be sent together. Sending only one is treated as invalid.

Include parameters

Some detail endpoints return a lean payload by default and expose heavier sections — errors, coverage, specs, artifacts, history — behind an opt-in ?include= parameter. This keeps default responses fast and predictable. Pass a comma-separated list of the sections you need, or all for everything the endpoint supports. Bundling exactly the data your view renders into a single round-trip avoids chaining follow-up requests from the client.
GET /{projectId}/test-runs/{runId}?include=errors,coverage
GET /{projectId}/test-runs/{runId}?include=all

Endpoints that accept include

Only these endpoints support the parameter. List endpoints, analytics, and dashboard endpoints do not.
EndpointSupported values
GET /test-runs/{runId}errors, coverage, specs, artifacts, all
GET /test-cases/{caseId}history, all

What each value adds

ValueEndpointAdds to response
errors/test-runs/{runId}errors object — grouped error failures with stack traces
coverage/test-runs/{runId}coverage object — code coverage metrics for the run
specs/test-runs/{runId}specs array — spec files with flattened test cases
artifacts/test-runs/{runId}artifacts object — authenticated download URLs (requires urls, see below)
history/test-cases/{caseId}history object — paginated list of past executions
allbothEverything supported on that endpoint

Artifacts pair with urls

include=artifacts on /test-runs/{runId} only returns URLs you explicitly request. Pass ?urls=url1,url2,... (max 100) alongside it:
GET /{projectId}/test-runs/{runId}?include=artifacts&urls=https://...,https://...
Without urls, the artifacts field returns null. URLs are time-limited and authenticated — don’t cache them long-term.

History paginated independently

include=history on /test-cases/{caseId} enables a second pagination for the history list, separate from any list-endpoint pagination. Three parameters control it, and all are ignored unless include=history (or include=all) is set:
ParameterDefaultDescription
historyPage1Page number within the history list (integer, ≥ 1).
historyLimit20Items per history page (integer, 1–100).
historyStatusFilter by outcome: passed, failed, flaky, or skipped.
curl "$BASE/${PROJECT_ID}/test-cases/${CASE_ID}?include=history&historyPage=2&historyLimit=50&historyStatus=failed" \
  -H "Authorization: Bearer $TESTDINO_API_TOKEN"
TipRequest only what you’ll render. include=all is convenient for exploration, but on production dashboards narrow it to the sections you actually display — smaller payloads, faster responses.