Quick start
Apply for an API key below. Pass it in the X-API-Key header with every request.
bash
GET https://testedclear.com/api/v1/verify?token=USER_TOKEN
X-API-Key: tc_live_your_key_here
javascript
const res = await fetch(
'https://testedclear.com/api/v1/verify?token=USER_TOKEN',
{ headers: { 'X-API-Key': 'tc_live_your_key_here' } }
)
const { verified, panel, tested_date, retest_by } = await res.json()
if (verified) {
showVerifiedBadge({ panel, tested_date, retest_by })
} else {
showRetestPrompt()
}
Verify endpoint
GET/api/v1/verify
| Parameter | Required | Description |
|---|
token | Yes | Public URL token from the user's TestedClear link |
format | No | json (default) or badge (HTML embed) |
Response schema
json
// 200 OK — verified and within testing window
{
"verified": true,
"panel": "Full STI Panel",
"tested_date": "2026-04-01",
"retest_by": "2026-07-01",
"issuer_type": "clinic",
"status": "current"
}
// 200 OK — retest window has passed
{
"verified": false,
"reason": "retest_due",
"retest_by": "2026-04-01"
}
// 200 OK — user revoked their credential
{
"verified": false,
"reason": "revoked"
}
// 404 Not Found — token does not match any credential
{
"verified": false,
"reason": "invalid_token"
}
All non-error responses return verified as a boolean — that is the only field you need to gate access on. Treat any non-200 status (other than 404) as a transient failure and retry.
Errors & status codes
Errors are returned as JSON with an error field and the status code in the HTTP response.
| Status | When it happens |
|---|
400 | Missing the `token` query parameter. |
401 | Missing `X-API-Key` header, or key format is not `tc_live_`, `tc_test_`, or `tc_badge_`, or key is not recognized. |
403 | Key has been suspended, or your application status is not approved. |
404 | Token does not match any credential in our system. |
429 | Free-tier monthly limit reached (100 calls). Upgrade to a paid tier. |
500 | Server error. Retry with exponential backoff. |
Every error response includes docs and apply URLs so that unfamiliar consumers of the API get pointed to the right place.
Auto-expiration
This is the key differentiator
The API returns verified: false automatically when the user's recommended retest date passes — no action needed from you or the user. A verified badge always means currently within testing window.
Rate limits & pricing
Usage is metered per-month per-key. Each successful call counts as one unit. Calls that return 4xx errors do not count against usage.
| Tier | Monthly | Included calls | Overage per call |
|---|
| Developer | Free | 100 | Hard cap at 100/month |
| Starter | $149 | 2,000 | $0.20 |
| Growth | $599 | 15,000 | $0.12 |
| Scale | $1,999 | 75,000 | $0.08 |
Developer tier is capped — once you hit 100 calls in a month, the endpoint returns 429 until the 1st of next month. Paid tiers allow overage and bill automatically through Stripe.
Badge embed (free)
The simplest integration — an iframe that renders a small verified badge. Uses format=badge on the standard verify endpoint and counts toward your monthly call quota.
html
<iframe
src="https://testedclear.com/api/v1/verify?token=USER_TOKEN&format=badge"
width="220" height="60" frameborder="0"
style="border:none;border-radius:10px;"
></iframe>
Badge Display API
For integrations that want richer badge options (SVG, HTML with display name, JSON for native rendering), use the dedicated Badge Display endpoint. Requires a tc_badge_ key — a separate key class from the verify-API tc_live_ key.
GET/api/v1/badge
| Parameter | Required | Description |
|---|
token | Yes | Public URL token from the user's TestedClear link |
format | No | json (default), html, or svg |
javascript
// JSON — render your own badge UI
const res = await fetch(
'https://testedclear.com/api/v1/badge?token=USER_TOKEN',
{ headers: { 'X-API-Key': 'tc_badge_your_key_here' } }
)
const { verified, display_name, panel, tested_date, retest_by, badge_url } = await res.json()
// SVG — drop-in image tag
// <img src="https://testedclear.com/api/v1/badge?token=USER_TOKEN&format=svg" />
The JSON response includes display_name when the user has set one — useful for rendering a personalized badge alongside a user's profile.
OAuth for linked apps
For apps that want verification status tied to a specific TestedClear user account (rather than a shareable link), use the OAuth 2.0 authorization code flow. The user consents in their TestedClear account, your app receives a long-lived access token, and you can then check verification status without the user ever sharing a link with you.
OAuth is a separate integration pathway — contact api@testedclear.com to onboard as a partner and receive a client_id and client_secret.
1. Redirect the user to authorize
bash
GET https://testedclear.com/api/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&response_type=code
&state=RANDOM_CSRF_TOKEN
The redirect_uri must exactly match one of the URIs registered with your partnership. After the user consents, they're redirected back with ?code=...&state=... appended.
2. Exchange the code for an access token
bash
POST https://testedclear.com/api/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "AUTH_CODE_FROM_CALLBACK",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uri": "https://yourapp.com/callback"
}
Auth codes expire after 10 minutes and are one-shot — they can only be exchanged once. Store the resulting access_token securely on your server.
3. Check verification status
bash
GET https://testedclear.com/api/oauth/me
Authorization: Bearer ACCESS_TOKEN
json
// Verified
{
"verified": true,
"panel": "Full STI Panel",
"tested_date": "2026-04-01",
"retest_by": "2026-07-01",
"issuer_type": "clinic"
}
// Retest due
{
"verified": false,
"reason": "retest_due",
"retest_by": "2026-04-01"
}
// No active credential
{
"verified": false,
"reason": "no_active_credential"
}
The OAuth endpoint returns the same verification shape as the verify API but tied to the user who consented — no token-sharing required. Same auto-expiration semantics apply.
Privacy
✓ ReturnedVerification status, test panel, test date, retest-by date
✗ Never returnedPhone number, name, email, raw results, user ID
Partnerships
We work with dating apps on deeper integrations — native badge display, user auth flows, and co-branded verification experiences.
Apply for API access
Free tier available. Enterprise pricing for high-volume integrations.