tdqs / api
Hosted API
The same scorer the playground and the CLI use, over HTTP, with a key from your account. Lint is synchronous. Scoring creates a report that the model fills in within seconds; poll it or open its page.
Basics
Base URL https://tdqs.dev/api/v1. Requests and responses are JSON. Every request carries Authorization: Bearer tdqs_… with a key from your account; signing in is with GitHub, and keys are free. A missing or rejected key answers 401; an exhausted allowance answers 429; every error body is { "error": { "code", "message" } }.
The tool definitions are what an MCP client sees from tools/list: send that result verbatim, or a bare array of tools, optionally with a serverName.
POST /lint
The deterministic stages only. Returns immediately; spends one call.
curl https://tdqs.dev/api/v1/lint \
-H 'Authorization: Bearer tdqs_...' \
-H 'Content-Type: application/json' \
-d '{ "serverName": "github-mcp", "tools": [ { "name": "list_issues", "description": "...", "inputSchema": { ... } } ] }'{
"lint": {
"specVersion": "1.2",
"generatedAt": "2026-09-01T12:00:00.000Z",
"server": { "name": "github-mcp", "toolCount": 1 },
"tools": [ { "name": "list_issues", "contextSignals": { ... }, "flags": [], "findings": [ ... ] } ],
"shadowCandidates": [],
"findings": [ { "tool": "list_issues", "rule": "missing-annotations", "severity": "warning", "message": "..." } ]
}
}POST /reports
Lints synchronously, stores the definitions, and queues the model stages. The response is the report's identity; the score arrives on it. Spends one call.
curl https://tdqs.dev/api/v1/reports \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer tdqs_...' \
-d @tools.json{
"uid": "k3n9x2p0qa",
"status": "queued",
"url": "https://tdqs.dev/reports/k3n9x2p0qa",
"apiUrl": "https://tdqs.dev/api/v1/reports/k3n9x2p0qa"
}GET /reports/:uid
The report as it stands. score is null until status is completed; a failed report carries a failureMessage. Poll every couple of seconds; the CLI's hosted mode does exactly this. Reading spends nothing.
{
"uid": "k3n9x2p0qa",
"status": "completed",
"requestedAt": "2026-09-01T12:00:00.000Z",
"completedAt": "2026-09-01T12:00:09.000Z",
"specVersion": "1.2",
"model": "deepseek/deepseek-v4-flash-0731",
"serverName": "github-mcp",
"toolCount": 10,
"lint": { ... },
"score": {
"specVersion": "1.2",
"model": "deepseek/deepseek-v4-flash-0731",
"server": { "name": "github-mcp", "toolCount": 10 },
"tools": [ { "name": "list_issues", "tdqs": 4.3, "tier": "A", "scores": { ... }, "justifications": { ... }, "flags": [], "smells": [] } ],
"serverScore": { "overallScore": 4.1, "overallTier": "A", "coherenceScore": 4.8, "descriptionQualityScore": 3.8, ... }
},
"failureMessage": null,
"url": "https://tdqs.dev/reports/k3n9x2p0qa"
}The shapes of lint and score are the package's published JSON Schemas, and every field name matches the specification's output format.
Limits
- per request
- Up to 200 tools; a definition set larger than 1 MB is refused.
- per account
- 30 calls per day, shared across the account's keys, resetting at 00:00 UTC. POST /lint and POST /reports spend one each; GET /reports/:uid is free. Every counted response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (the next reset, as unix seconds); the account overview shows the same balance.
- model
- The operator's choice, named on every report; scores are only comparable within one rubric+model pair.