API reference/phone

Monitor a phone list on a schedule

POST/api/v1/phone/monitors

authentication
Secret API key as a bearer token
billing
Creation is free. Each run bills every number like a single lookup: 1 credit for a fresh valid answer, plus the live-check credits when the check answered; invalid, unassigned, suppressed and 7-day repeats are free.

Saves up to 500 numbers and re-checks them every week or every month. The first run starts right away and only sets the baseline; from the next run on, every number that was valid last time and is now invalid, in a block nobody holds or unreachable counts as decayed and is reported to the account's email. Runs honour the suppression list and the 7-day repeat cache. With live_check true, runs ask for the live carrier check whenever the service has it enabled, so decay also covers handsets that stopped answering.

Answers 201 with the monitor. The baseline run is queued, not finished: poll the monitor for last_run_at and last_summary.

Request body

field type description
namerequiredstringA label for the dashboard and the decay alerts. At most 100 characters.
numbersrequiredstring[]1 to 500 numbers in any notation; each item at most 32 characters.
cadencerequiredstringHow often the list is re-checked. One of: weekly, monthly.
countrystring | nullThe region for numbers written without a calling code.
live_checkbooleanAsk for the live carrier check on every run where the service has it enabled. Default: .

Example request

curl -X POST https://spaw.co/api/v1/phone/monitors \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "SMS audience",
  "numbers": [
    "+44 7911 012345",
    "020 7946 0018"
  ],
  "cadence": "monthly",
  "country": "GB"
}'
const response = await fetch('https://spaw.co/api/v1/phone/monitors', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_…',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "SMS audience",
    "numbers": [
      "+44 7911 012345",
      "020 7946 0018"
    ],
    "cadence": "monthly",
    "country": "GB"
  }),
});
const result = await response.json();
import requests

response = requests.post(
    'https://spaw.co/api/v1/phone/monitors',
    headers={'Authorization': 'Bearer sk_live_…'},
    json={
        'name': 'SMS audience',
        'numbers': [
            '+44 7911 012345',
            '020 7946 0018'
        ],
        'cadence': 'monthly',
        'country': 'GB'
    },
)
result = response.json()
$ch = curl_init('https://spaw.co/api/v1/phone/monitors');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_live_…', 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'SMS audience',
        'numbers' => [
            '+44 7911 012345',
            '020 7946 0018'
        ],
        'cadence' => 'monthly',
        'country' => 'GB'
    ]),
]);
$result = json_decode(curl_exec($ch), true);

Responses

201The monitor was saved and its baseline run queued.

{
    "success": true,
    "data": {
        "monitor": {
            "id": 7,
            "name": "SMS audience",
            "cadence": "monthly",
            "number_count": 2,
            "country": "GB",
            "live_check": false,
            "next_run_at": "2026-10-05T10:12:44+00:00",
            "last_run_at": null,
            "last_summary": null,
            "created_at": "2026-09-05T10:12:44+00:00"
        }
    }
}

401The key is missing, malformed, or revoked.

{
    "success": false,
    "error": {
        "code": "UNAUTHENTICATED",
        "message": "Provide a valid API key as a bearer token.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

422The request body could not be validated; `error.errors` lists the fields.

{
    "success": false,
    "error": {
        "code": "VALIDATION_FAILED",
        "message": "The email field is required.",
        "errors": {
            "email": [
                "The email field is required."
            ]
        },
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

429Over 5 requests per second for the key. Retry after the limit resets.

{
    "success": false,
    "error": {
        "code": "RATE_LIMITED",
        "message": "Too many requests. Retry after the limit resets.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}

Error codes

Failures answer { success: false, error: { code, message, request_id } }. Each code has its own page.

markdown version·openapi.json