UK TPS/CTPS screening as an API.
One request screens a number, or up to 1,000 at once, against the TPS and CTPS registers. Sign in, create a test key, and make your first call against fixtures before you buy anything.
What it does
Single and bulk lookups
One endpoint, one number or up to 1,000 per request, answered in the same response.
Your own suppression list
Keep the opt-outs you have collected yourself on your account, next to the register checks. Add, list and remove numbers with the same key.
Audit-ready response logs
Every screen result is stored and addressable by id, with the timestamp and the key that made the call. Pull it back later as evidence.
Quick example
Base URL: https://api.tpsclear.co.uk
Request
POST https://api.tpsclear.co.uk/v1/screen
Authorization: Bearer tpsc_live_xxx # use tpsc_test_* in CI/sandbox
Content-Type: application/json
{
"phoneNumbers": ["+441134960001", "+441134960002"],
"campaignRef": "spring-outbound-2026"
}campaignRef: Optional reference stored on the audit record for this call, e.g. a campaign or client identifier. Max 120 characters. When supplied it is echoed at the top level of the response beside results. A single number can also be sent as "phone": "+44..". Numbers can be in national (0113 496 0001) or international format.
Response
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: 6f1d2c9a-4b3e-4f2a-9c1d-8e7b6a5f4d3c
X-RateLimit-Remaining: 4998
X-RateLimit-Reset: 1792713600
{
"results": [
{
"phoneNumber": "+441134960001",
"tpsListed": false,
"ctpsListed": false,
"status": "clean",
"checkedAt": "2026-10-22T10:21:01.204Z",
"recheckBy": "2026-11-18",
"tpsFileDate": "2026-10-22",
"ctpsFileDate": "2026-10-22"
},
{
"phoneNumber": "+441134960002",
"tpsListed": true,
"ctpsListed": false,
"status": "tps-listed",
"checkedAt": "2026-10-22T10:21:01.204Z",
"recheckBy": "2026-11-18",
"tpsFileDate": "2026-10-22",
"ctpsFileDate": "2026-10-22"
}
],
"campaignRef": "spring-outbound-2026"
}There is one result per number sent, in the order sent. The request id is returned only in the X-Request-Id header, not in the body; keep it with your own record of the call, because it is how you fetch the stored audit record later. The header is missing only if the audit record could not be written, which the call does not fail on.
The numbers above are test fixtures: the same request with a tpsc_test_* key gives the same verdicts, free.
Result fields
| Field | Type | Meaning |
|---|---|---|
phoneNumber | string | The number in E.164 form (+441134960001). An entry that is not a UK number is echoed back as sent, cut to 40 characters. |
tpsListed | boolean | On the TPS (individuals and sole traders). |
ctpsListed | boolean | On the CTPS (companies and other organisations). |
status | string | One of the six statuses below. |
checkedAt | ISO 8601, UTC | When the register was checked for this number. For a partner verdict reused from our cache, it is the time of the original check (see where a result came from). For suppressed and consented, it is the time of the request. |
recheckBy | YYYY-MM-DD, UTC | The date to screen this number again, before calling it that day: 27 days after the UTC date of checkedAt (see re-screening). Absent on unknown, suppressed and consented. |
tpsFileDate | YYYY-MM-DD | The date of the TPS file the number was checked against. Present when our own register answered, and on test keys. Absent otherwise. |
ctpsFileDate | YYYY-MM-DD | The date of the CTPS file, on the same rule. The TPS and CTPS are separate files, so the two dates can differ: if the DMA has a problem with one day's file, that register is served from the previous day's file until it is fixed. |
Statuses
| Status | Meaning | Charged |
|---|---|---|
clean | On neither register. | Yes |
tps-listed | On the TPS. | Yes |
ctps-listed | On the CTPS. A number on both registers comes back ctps-listed with both flags true. | Yes |
unknown | No verdict. Either the entry is not a valid UK number, so it was not looked up, or the register lookup failed. A failure is counted in providerFailures on the audit record and is worth retrying. Treat unknown as not screened. | No; a failed lookup is refunded |
suppressed | On your own suppression list. Not looked up. A number on both of your lists comes back suppressed. | No |
consented | On your own consent list. Not looked up. You only see this status if you add numbers to the consent list. | No |
One credit covers one check against both registers. A number sent twice in one request is charged once.
Where a result came from
A live result comes from our own copy of the TPS and CTPS registers, loaded from the DMA's daily files, or from a licensed partner. A result from our register carries tpsFileDate and ctpsFileDate, and is never reused: every one is a fresh lookup made during the call, so its checkedAt falls just before the audit record's createdAt.
If our register cannot answer, a licensed partner checks the number instead. A partner result carries no file dates. It may be reused from our cache for up to 27 days, and then its checkedAt is the time of the original partner check, earlier than createdAt. Accounts opened from 19 October 2026 go to our standby register server first, so a partner answers only if both of our servers are down. Accounts opened earlier can ask to move to the same route.
So, on a live key: file dates present means our register answered; no file dates means a partner answered, and a checkedAt well before createdAt means that partner verdict was reused.
Re-screening every 28 days
Under PECR regulation 21(3), a number that joins the TPS or CTPS can be called for 28 days after it is listed, and from then on calling it is a breach. A clean result therefore holds for 28 days. Register data reaches us in a daily file, so a check can be a day behind, which is why recheckBy falls on day 27.
The API does not re-screen your numbers for you. Store recheckBy with each number and send the number to POST /v1/screen again on or before that date. A daily job that screens every number whose recheckBy is today or earlier keeps the whole list current. In Make, Zapier or n8n, that is a schedule trigger that runs once a day, followed by a search for the due numbers and the TPSClear screen step. Each re-screen counts as a check.
Endpoints
| Endpoint | Purpose |
|---|---|
POST /v1/screen | Single or bulk lookup. Synchronous response with TPS and CTPS status per number. |
GET /v1/screen/{id} | Fetch a past screen result by id for audit purposes. |
GET /v1/usage | Current-window usage for your key plus rolling 30-day totals for your account. |
GET /v1/me | The calling key's name, environment, plan and scopes. Works with any scope, for testing a connection. |
POST /v1/keys/rotate | Mint a replacement token for your key and (by default) revoke the old one immediately. |
GET /v1/suppressions | Your own do-not-call list: the total count and the 100 most recent entries. A number on the list comes back from POST /v1/screen with status suppressed, is not screened and uses no lookup. |
POST /v1/suppressions | Add up to 10,000 numbers per request, as {"numbers": [..]}, as {"entries": [..]} with a reason and a datetime per number, or as a CSV body (see below). Numbers already on the list are ignored. A list holds up to 100,000. |
DELETE /v1/suppressions | Remove one number, sent as {"number": "+44..", "note": ".."} in the body. The note, why it came off the list, is optional. List calls use no lookups. |
GET /v1/consents | Your consent list: numbers you hold consent to call. A number on it comes back from POST /v1/screen with status consented, is not screened and uses no lookup. A number on both lists comes back suppressed. |
POST /v1/consents | Add numbers, with the same bodies and limits as POST /v1/suppressions. Put your consent evidence in the reason. |
DELETE /v1/consents | Remove one number when consent is withdrawn, with the same body as DELETE /v1/suppressions. |
POST /v1/dead-number | Ask the mobile network whether up to 500 numbers are live, switched off or dead. Live key only, prepaid checks. See dead number checks. |
GET /v1/dead-number | The dead number checks left on your account. |
Three calls spend prepaid checks: POST /v1/screen with a live key, POST /v1/dncr and POST /v1/dead-number. Every other endpoint, including GET /v1/me, GET /v1/usage and GET /v1/screen/{id}, spends nothing and starts no screening.
Adding numbers with a reason and a date
Each entry takes a number, an optional reason (up to 500 characters) and an optional datetime: when the opt-out or consent happened. Without one, the date is when you added the number. Send ISO 8601 (2026-09-18T11:02:00Z) or UK day-first 18/09/2026 12:02; a time with no zone is UK time. US month-first dates, dates before 2000 and dates in the future are rejected.
POST /v1/consents
Content-Type: application/json
{ "entries": [ { "number": "01134960001", "reason": "Web form, ref WF-2291", "datetime": "2026-09-18T11:02:00Z" } ] }Or send the file as it is, with Content-Type: text/csv. Columns are number,reason,datetime; the header row and the last two columns are optional, and quoted fields may hold commas. The body can be up to 2 MB.
POST /v1/suppressions
Content-Type: text/csv
number,reason,datetime
01134960001,"Asked not to be called, by phone",18/09/2026 12:02
07700900123,Client removal list,List responses
Both lists answer in the same shape; the consent list returns consents where the suppression list returns suppressions. A POST imports every good row and reports the rest: invalid holds up to 50 rejected values, invalidRows gives the line and the reason for each, and invalidCount counts them all.
GET -> { "count": 42, "suppressions": [ { "phoneNumber": "+441134960001", "note": "Asked not to be called", "createdAt": "2026-09-18T11:02:00Z", "occurredAt": "2026-09-18T11:02:00Z" } ] }
POST -> { "ok": true, "added": 2, "alreadyListed": 1, "invalid": ["0113"], "invalidRows": [ { "line": 4, "value": "0113", "reason": "not a valid UK phone number" } ], "invalidCount": 1, "total": 44 }
DELETE -> { "ok": true, "removed": true }Retrieve a screening record
Every POST /v1/screen response carries an X-Request-Id header. That id addresses the stored audit record: pull it back any time with a GET. Records belong to your account; an id that does not exist, or belongs to someone else, returns 404.
summary.unknown counts every number that came back without a register verdict, which includes numbers rejected as invalid UK numbers before any lookup was made. providerFailures is the subset where the check was attempted and the register lookup failed; those are refunded and are worth retrying. summary.suppressed counts numbers held back by your suppression list and summary.consented numbers held back by your consent list; they are not looked up and not charged.
GET https://api.tpsclear.co.uk/v1/screen/6f1d2c9a-4b3e-4f2a-9c1d-8e7b6a5f4d3c
Authorization: Bearer tpsc_live_xxxResponse
One envelope per call, with the request id at the top level and the results exactly as the original call returned them. This one holds a result from our register and a partner result reused from the cache (no file dates, and a checkedAt before createdAt).
{
"id": "6f1d2c9a-4b3e-4f2a-9c1d-8e7b6a5f4d3c",
"createdAt": "2026-10-22T10:21:01.733Z",
"environment": "live",
"lookups": 2,
"campaignRef": "spring-outbound-2026",
"summary": { "clean": 2, "tpsListed": 0, "ctpsListed": 0, "unknown": 0, "suppressed": 0, "consented": 0 },
"providerFailures": 0,
"results": [
{
"phoneNumber": "+4420XXXXXXXX",
"tpsListed": false,
"ctpsListed": false,
"status": "clean",
"checkedAt": "2026-10-22T10:21:01.204Z",
"recheckBy": "2026-11-18",
"tpsFileDate": "2026-10-22",
"ctpsFileDate": "2026-10-22"
},
{
"phoneNumber": "+447XXXXXXXXX",
"tpsListed": false,
"ctpsListed": false,
"status": "clean",
"checkedAt": "2026-10-05T14:11:05.230Z",
"recheckBy": "2026-11-01"
}
]
}| Field | Meaning |
|---|---|
id | The request id, the same value as the X-Request-Id header on the original call. Assigned once and never changed. |
createdAt | When the record was written, at the end of the call. |
environment | live or test, from the key that made the call. |
lookups | Distinct valid numbers sent for a register check. On a live key this is what the call cost, before any refund. |
summary | Results counted by status. |
providerFailures | Numbers whose register lookup failed; refunded. |
results | One entry per number sent, in the order sent, with the result fields. |
Check usage
One GET tells you where your key stands in the current fair-use window and what your account has consumed over the last 30 days. Reading usage never consumes any of it.
GET https://api.tpsclear.co.uk/v1/usage
Authorization: Bearer tpsc_live_xxxResponse
{
"key": { "last4": "k9Qz", "name": "Acme production" },
"environment": "live",
"plan": "beta",
"window": {
"limit": 5000,
"windowSecs": 86400,
"used": 1240,
"remaining": 3760,
"reset": 1751932800
},
"last30d": { "calls": 87, "lookups": 21430 },
"uk_credits": 8760
}window.used counts lookups, not requests, against the same fixed window the 429 limiter uses; window.reset is the epoch second the window rolls over. On very high-volume accounts last30d may include "approximate": true when the lookup total is summed from a capped sample of calls. uk_credits is the prepaid UK checks left on a live key with no CRM portal behind it, and null otherwise.
Identify a key
GET /v1/me returns the calling key's identity and the scopes it holds, with no usage figures. Any valid key can call it, whatever its scopes, so it is the endpoint to test a connection against. The IP allowlist still applies.
GET https://api.tpsclear.co.uk/v1/me
Authorization: Bearer tpsc_live_xxxResponse
{
"key": { "last4": "k9Qz", "name": "Acme production" },
"environment": "live",
"plan": "credits",
"scopes": ["screen", "suppress"]
}Rotate a key
Rotate the key you are calling with. You get a brand-new token on the same plan and limits, shown once in the response and never again; store it before the connection closes. By default the old key is revoked immediately. Send { "revokeOld": false } to keep the old key alive while you cut services over, then retire it later by rotating with it again.
POST https://api.tpsclear.co.uk/v1/keys/rotate
Authorization: Bearer tpsc_live_xxx
Content-Type: application/json
{
"revokeOld": true
}Response
{
"ok": true,
"token": "tpsc_live_...full token, shown only once...",
"tokenPrefix": "tpsc_live_AbC123dEf456",
"last4": "x7Pq",
"revokedOld": true,
"note": "Store this token now; it is shown only once. ..."
}Auth
Bearer tokens in the Authorization header. Tokens are environment-scoped: tpsc_test_* hits a sandbox that returns deterministic fixtures, tpsc_live_* hits the live register data. The suppression and consent lists are shared by both, so a test key can read them but not change them: POST and DELETE on /v1/suppressions and /v1/consents return 403 test_key_read_only. Tokens can be rotated self-serve via POST /v1/keys/rotate or revoked on request at any time; revocation takes effect immediately.
Issue your own keys from your account. Sign in with a link emailed to your work address, buy a pack of checks, and create, list or revoke keys yourself. Test keys are free, within a daily fair-use limit, so you can build and debug against fixtures before you buy anything.
If you already run TPSClear inside HubSpot, you can issue keys from the app settings page instead (Settings, Integrations, Connected Apps, TPSClear, the API tab), behind your HubSpot login. A key issued that way follows the subscription on the connected portal, and its lookups count towards the same plan allowance as screening driven from the CRM. A key issued from your account draws on your credit balance instead. Either kind works against the same endpoints.
Scopes
Each key holds one or more scopes, and each endpoint needs one. A key holds every scope unless you untick some when you create it on your account.
screen: POST /v1/screen, and GET /v1/screen/{id} to fetch a past result.suppress: Read and change the suppression list at /v1/suppressions.consents: Read and change the consent list at /v1/consents.usage: Read lookup usage at GET /v1/usage.dncr: Wash numbers against the Australian Do Not Call Register at /v1/dncr.dead_number: Check whether mobiles are live, switched off or dead at /v1/dead-number.keys: Rotate this key with POST /v1/keys/rotate.
A call to an endpoint the key has no scope for returns 403 insufficient_scope, with a message naming the scope it needs. GET /v1/me needs no scope.
IP allowlist
A key can also be limited to the addresses it is used from: up to 20 entries, each an exact IPv4 or IPv6 address or a CIDR range (203.0.113.10, 198.51.100.0/24, 2001:db8::/48). Wildcards and host names are not accepted. With no allowlist, any address can use the key. A call from outside the list returns 403 ip_not_allowed. Rotating a key with POST /v1/keys/rotate keeps both its scopes and its allowlist.
Auth errors
401unauthorized: noAuthorization: Bearerheader.401invalid_api_key: the key is malformed, revoked or unknown.402subscription_required: the account or connected HubSpot subscription is not active.403insufficient_scope: the key lacks the scope this endpoint needs.403ip_not_allowed: the call came from an address outside the key's allowlist.403test_key_read_only: a test key tried to change the suppression or consent list.403client_name_required: a live key on an account that has not named the business it screens for. See client name.429rate_limited: too many requests on this key; wait forRetry-After.
Recommended practice
- Store tokens in your platform's secret manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Doppler, or the equivalent on your stack). Never commit tokens to source control.
- Use the test token in CI and staging. Live tokens belong only in production runtime environments.
- Rotate on staff changes. If a token leaks, revoke first, debug after.
- Give each integration its own key with only the scopes it uses, and an IP allowlist where it runs from fixed addresses.
Test keys and fixtures
A tpsc_test_* key screens against the fixed numbers below instead of the registers. It is free and spends no credits; its lookups count only towards the key's daily fair-use budget. Everything else matches a live call: the response shape, the X-Request-Id header, and an audit record you can fetch with GET /v1/screen/{id}, marked "environment": "test". Test results carry tpsFileDate and ctpsFileDate set to the day of the call, so a parser built against a test key reads a live register result unchanged.
| Number | Status on a test key |
|---|---|
+441134960002 | tps-listed |
+441134960003 | ctps-listed (on both registers) |
+441134960004 | ctps-listed |
+442079460018 | ctps-listed |
| Any other valid UK number | clean |
Not a UK number, e.g. 0113 | unknown |
Your suppression and consent lists apply to test keys too, so a listed number comes back suppressed or consented. A test key can read those lists but not change them. Australian washes and dead number checks have no test mode and need a live key.
Client name (licence requirement)
Voll Studios Ltd, the company behind TPSClear, is licensed by the DMA as a TPS and CTPS list cleaner. The licence requires a monthly declaration naming each client, so a live API key must belong to an account that names the business it screens numbers for.
A live key on an account with no business name gets 403:
{
"error": "client_name_required",
"message": "...",
"account_url": "https://tpsclear.co.uk/account"
}Set the name once, with the key itself or on your account. Use the legal name of the business the numbers are screened for.
curl -X PATCH https://api.tpsclear.co.uk/v1/me \
-H "Authorization: Bearer tpsc_live_xxx" \
-H "Content-Type: application/json" \
-d '{"company_name": "Acme Outbound Ltd"}'Rate limits and cost
A live check costs one credit from your prepaid balance. Limits are counted in lookups, not requests, so a bulk call of 500 numbers counts as 500.
A self-serve key has a daily lookup budget of 5,000, and the account can screen 5,000 numbers a day across all its keys. Both reset at 00:00 UTC. Past either, the call returns 429 with a Retry-After header, and every response carries X-RateLimit-Remaining and X-RateLimit-Reset. Contact us if you need a higher limit.
Keys issued from a connected HubSpot portal draw on the portal's plan allowance instead, shared with CRM-side screening, and can screen 25,000 numbers a day. With Allow overage off, reaching it returns 429 with {"error": "allowance_reached"} until the allowance renews.
Audit trail
Every screen call, single or bulk, is persisted with its full result set, the timestamp it was checked, and the token that made the call. Pull any prior result back via GET /v1/screen/{id}. The stored record is your evidence that the number was screened before the call.
Records are kept for 24 months, then deleted by a job that runs daily. If you first bought before 21 October 2026 they are kept for 12 months, the period in the DPA in force when you bought. You can ask for earlier deletion at any time at privacy@tpsclear.co.uk.
Screening log CSV
To keep the whole log, use Email me my screening log on your account page: a CSV with one row per number checked, newest first, up to 250,000 rows. Save a copy if you need records for longer than the retention period.
checked_at,number,result,tps_listed,ctps_listed,tps_file_date,ctps_file_date,campaign_ref,request_id,environment
2026-10-22T10:21:01.204Z,"=""+4420XXXXXXXX""",clean,false,false,2026-10-22,2026-10-22,spring-outbound-2026,6f1d2c9a-4b3e-4f2a-9c1d-8e7b6a5f4d3c,livechecked_at,result,tps_listed,ctps_listed,tps_file_date,ctps_file_date: the result fieldscheckedAt,status,tpsListed,ctpsListed,tpsFileDateandctpsFileDate, unchanged. The file dates are empty where the result had none.number: the E.164 number, written as a formula string so spreadsheets keep the leading+.campaign_ref,request_id,environment: from the audit record the row belongs to.
Australian Do Not Call Register
Wash Australian numbers against ACMA's Do Not Call Register on our ACMA subscription. Send up to 500 numbers per request, in international or national format. Each valid number sent uses one prepaid wash, rechecks included. Invalid numbers are not sent and cost nothing, and a number the register does not answer is not charged. Australian washes need a live key, as the register has no test mode.
Prices
| Washes | Price | Per wash |
|---|---|---|
| 100 | A$4 | 4.00c |
| 250 | A$9 | 3.60c |
| 500 | A$14 | 2.80c |
| 1,000 | A$24 | 2.40c |
| 2,500 | A$48 | 1.92c |
| 5,000 | A$85 | 1.70c |
| 10,000 | A$145 | 1.45c |
| 25,000 | A$310 | 1.24c |
| 50,000 | A$600 | 1.20c |
| 100,000 | A$1,170 | 1.17c |
| 250,000 | A$2,750 (invoiced) | 1.10c |
| 500,000 | A$5,250 (invoiced) | 1.05c |
| 1,000,000+ | Contact us |
Prices in Australian dollars, prepaid, valid 12 months from purchase. Packs of 25,000 washes and above are switched on by hand within one working day; smaller packs are usually on your key straight away. 250,000 and 500,000 are invoiced.
Buy washes from your account, the same page that sells UK checks and issues keys.
POST https://api.tpsclear.co.uk/v1/dncr
Authorization: Bearer tpsc_live_xxx
Content-Type: application/json
{ "phoneNumbers": ["+61 491 570 156", "02 6219 5555"] }Response
{
"results": [
{
"phoneNumber": "+61491570156",
"status": "on_register",
"washedAt": "2026-10-10T09:00:00.000Z",
"validUntil": "2026-11-09T09:00:00.000Z",
"washRef": "1042"
},
...
],
"washesCharged": 2
}status is on_register, not_on_register, invalid or unknown (no answer from the register, not charged). A geographic or mobile number outside every range ACMA has allocated to a carrier is not sent and not charged; it comes back invalid with "reason": "unallocated". Treat on_register as do not call: the register also answers it for numbers that are not in service. A wash is valid for 30 days, so validUntil is the last date you can call on it. washRef is our reference for the wash with ACMA, kept with each result as your evidence. Business numbers cannot be registered, so not_on_register on a business line says nothing either way.
GET /v1/dncr with your key returns { "washesRemaining": 2440 }. A request needing more washes than the balance gets 402 insufficient_credits and is not sent.
Dead number checks
Ask the mobile network whether a mobile is live, switched off or dead, which network holds it and whether it has been ported. The technical name is an HLR lookup. It works for mobiles in any country. Send up to 500 numbers per request, in international format; a number without a country code is read as a UK number. Every request is a fresh lookup. Dead number checks need a live key with the dead_number scope and are paid from prepaid checks, separate from TPS credits.
Prices
| Checks | Price | Per check |
|---|---|---|
| 100 | £1 | 1p |
| 250 | £2.50 | 1p |
| 500 | £5 | 1p |
| 1,000 | £10 (invoiced) | 1p |
Ex VAT, prepaid, valid 12 months. A pack that cannot be bought by card is invoiced: contact us.
Buy checks from your account, the same page that sells UK checks and issues keys.
POST https://api.tpsclear.co.uk/v1/dead-number
Authorization: Bearer tpsc_live_xxx
Content-Type: application/json
{ "numbers": ["07700 900123", "+44 20 7946 0000"] }Response
{
"results": [
{
"input": "07700 900123",
"e164": "+447700900123",
"status": "live",
"network": "EE",
"ported": false,
"country": "GB",
"charged": true,
"checkedAt": "2026-10-16T09:00:00.000Z"
},
...
],
"checksCharged": 1,
"checksRemaining": 2499
}There is one result per number sent, in the order sent. e164 and country (an ISO country code) are null for an invalid number. network is the network that holds the number today and ported says whether it has moved from the network that first issued it; both are null when the lookup did not report them. charged says whether that number cost checks, and checkedAt is when the network answered, or null when the number was not looked up. A result has a shelf life, so keep the date with it and check again before relying on an old one.
Statuses
| Status | Meaning |
|---|---|
live | A SIM holds the number and the network can reach it. |
switched_off | The number is in service, but the phone is off or out of coverage. Treat it as live. |
dead | The network says no SIM holds the number. |
undetermined | No live status came back, for example from a network that does not give one. Treat it as not checked, never as dead. |
not_mobile | A landline or other number that is not a mobile. |
invalid | Not a valid phone number. |
What is charged
- A mobile costs one check. A US number costs 2, and is looked up even when it turns out to be a landline, so a US number can come back
not_mobilewithchargedtrue. - Other landlines and invalid numbers are not looked up and are free.
- An
undeterminedanswer from the network is charged. A number the lookup did not answer at all also comes backundetermined, withchargedfalse, and its checks are credited back. - A number sent twice in one request is looked up and charged once.
checksCharged is the total for the request and checksRemaining is the balance after it. The checks a request could need are reserved before the first lookup, and whatever was not used goes back before the response. GET /v1/dead-number with your key returns { "checksRemaining": 249 }.
Errors
400live_key_required: a test key. The network lookup has no test mode.400batch_too_large: more than 500 numbers in one request.400invalid_request: nonumbersarray, or an empty one;400invalid_json: the body is not a JSON object;413payload_too_large: the body is over 32 KB.402insufficient_credits: the request needs more checks than the balance holds. The response carriesrequiredandbalance, and nothing is looked up.403insufficient_scope: the key lacks thedead_numberscope.429rate_limitedordaily_cap_reached: the key's lookup limit or the account's daily limit, which dead number checks share with UK screening. Nothing is charged.503lookup_unavailable: the network lookup did not answer. The checks for the request are credited back; try again shortly.
MCP server
The same checks are available to AI agents as a Model Context Protocol server, over Streamable HTTP at https://tpsclear.co.uk/api/mcp. Authenticate with a live key in the Authorization header: Authorization: Bearer tpsc_live_xxx, or sign in with OAuth 2.1 (discovery at /.well-known/oauth-protected-resource/api/mcp, dynamic client registration, PKCE S256), which is how Claude connects. Calls draw on the same prepaid balances as the REST API either way. An overview for people choosing a client, with the Claude setup steps, is at tpsclear.co.uk/mcp.
Tools
screen_numbers: Screen UK numbers against the TPS and CTPS registers.wash_au_numbers: Wash Australian numbers against the Do Not Call Register.check_dead_numbers: Live mobile network (HLR) lookups: live, switched off or dead.get_account: The account's balances and company name.set_company_name: Set the legal name of the business the account screens numbers for.buy_credits: Returns a Stripe Checkout payment link for a named pack. A person pays it by card; the tool charges nothing. The credits land on the connected account.list_packs: Every prepaid pack on sale, with the id buy_credits takes, what it buys, its price ex VAT and how long it lasts.get_screening_history: Recent UK screening calls and every purchase, draw and refund on the prepaid balances, newest first. No phone numbers are returned.
Claude Code
claude mcp add --transport http tpsclear https://tpsclear.co.uk/api/mcp --header "Authorization: Bearer tpsc_live_..."Other MCP clients
Clients that read a JSON config take the server like this:
{
"mcpServers": {
"tpsclear": {
"type": "http",
"url": "https://tpsclear.co.uk/api/mcp",
"headers": { "Authorization": "Bearer tpsc_live_..." }
}
}
}Licence, data and contracts
- Licence. Voll Studios Ltd, which runs TPSClear, is licensed by the DMA as a TPS and CTPS list cleaner. Our entries are in the DMA's directories of licensed TPS and CTPS list cleaners.
- Data Processing Agreement. Published at tpsclear.co.uk/dpa and part of the Terms. We also send a countersigned copy naming your company on request.
- Where data is processed. Register screening runs in the United Kingdom. The application database is in an EU region. Every sub-processor, its location and the transfer mechanism are on the sub-processor list.
- Retention. Audit records: 24 months, or 12 on accounts that first bought before 21 October 2026. Cached partner verdicts: deleted no later than 60 days after the check. Account records: the life of the account plus 12 months. Names are never read or stored. Detail is in the privacy notice.
Get an API key
Sign in with a link emailed to your work address and create a key straight away. A test key is free and screens against fixtures. A live key screens the real TPS and CTPS registers and costs one credit per check.
Checks are sold in prepaid packs from £9, and they last 12 months. Screening from inside a CRM is a monthly plan instead, from £95. Weighing up building this yourself? Read the build-vs-buy breakdown.
