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. The preset parameter name depends on the endpoint: most use dateRange, the test runs list uses period. Both take the same preset values and behave identically.
ParameterEndpointsExample
dateRangeDashboard and analytics endpointsGET /{projectId}/dashboard?dateRange=30d
periodList test runsGET /{projectId}/test-runs?period=30d
Predefined ranges — same values for both dateRange and period:
ValueMeaning
7dLast 7 days
14dLast 14 days
30dLast 30 days
60dLast 60 days
90dLast 90 days
Custom ranges — pass startDate + endDate (ISO 8601 dates) on either endpoint:
GET /{projectId}/dashboard?startDate=2026-03-01&endDate=2026-03-31

Precedence

If both the preset and startDate/endDate are provided, the preset takes precedence and the custom dates are ignored. Pick one:
If you want…SendDon’t send
Preset rangedateRange=30d or period=30dstartDate, endDate
Custom rangestartDate=..., endDate=...dateRange, period
NotestartDate and endDate must be sent together. Sending only one is treated as invalid. Check each endpoint’s reference page for which preset parameter it accepts.

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.