Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/numbers-online:numbers-online-outbound-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Numbers Online Phone Intelligence Outbound API
description: Phone number parsing, validation, and inbound caller-intelligence as a supplementary signal.
version: 1.0.0
contact:
name: Phone Numbers Online
url: https://numbers.online
servers:
- url: https://numbers.online
description: Production server
- url: http://localhost:3000
description: Development server
tags:
- name: Outbound
description: Outbound pre-call checks (scrub + call-provenance) and number enrollment for spoofing-defense
paths:
/api/v1/outbound/lookup:
post:
tags:
- Outbound
summary: Outbound pre-call check (scrub + provenance)
description: 'An outbound integration''s pre-call check on a destination. Returns the M1 pre-call scrub on `to` (SUPPRESS | NO_MATCH | UNKNOWN) plus three more supplementary signals: a compliance signal; an outbound `dial_risk` read — a low-confidence estimate of the cost/abuse risk of DIALING `to` (premium / satellite / international / IRSF-cover structural prior, plus any dynamic fraud observations); and a `cost_estimate` — the indicative RETAIL cost to reach `to`, priced for BOTH channels (voice per minute, SMS per message) from aggregated provider list-price decks, with a per-provider breakdown. All are supplementary signals, NEVER a consent grant or a block verdict: NO_MATCH is not permission to call and a dial_risk level is not a block — you remain responsible for your own lawful basis and dialing decision. For an ENROLLED caller (a verified business number you enrolled via /api/v1/outbound/enroll), it also records a hashed, short-TTL (from -> to) call-provenance edge: a self-incriminating record of who you actually dialled, used at report time to hold your number accountable for calls it DID place and to shield it from reports about calls it did NOT place (spoofing). Both ends are hashed, server-only, and never surfaced. Non-enrolled callers still get the scrub; no edge is written. Requires an API key with the `precall` use case. Free (bundled); not metered. Supplementary signal only — no per-call verdict. This is the canonical spelling — the outbound mirror of /api/v1/inbound/lookup; the original /api/v1/precall/lookup stays a permanent alias served by the same handler.'
operationId: precallLookup
security:
- ApiKeyAuth: []
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- to
properties:
to:
type: string
description: The destination number being called.
example: '+14155551212'
from:
type: string
description: Your own calling number. A provenance edge is recorded only when this is a number you have enrolled.
example: '+442071838750'
context:
type: string
enum:
- outbound_voice
- outbound_sms
description: 'The channel you are about to use. Drives which suppression list is scrubbed: outbound_sms scrubs SMS preferences, anything else scrubs voice. Echoed back as `dnc_channel`.'
responses:
'200':
description: Pre-call scrub (+ provenance edge when enrolled)
content:
application/json:
schema:
type: object
properties:
schema_version:
type: string
example: '2026-06-23'
to:
type: string
dnc:
type: string
enum:
- SUPPRESS
- NO_MATCH
- UNKNOWN
dnc_channel:
type: string
enum:
- voice
- sms
description: Which suppression list the `dnc` answer was scrubbed against (derived from `context`).
dnc_note:
type: string
compliance:
type:
- object
- 'null'
properties:
dnc_status:
type: string
reassigned_status:
type: string
dial_risk:
type:
- object
- 'null'
description: 'Outbound dial-risk signal: the cost/abuse risk of DIALING `to` (structural prior plus any dynamic fraud observations). Supplementary signal, not a block verdict. Null if scoring failed (fail-open).'
properties:
risk:
type: integer
description: Risk score 0–100.
example: 6
level:
type: string
enum:
- low
- medium
- high
description: 'Banded risk: low (<40), medium (40–69), high (≥70).'
model:
type: string
enum:
- dial_structural
description: 'Scoring model (§ Risk models): the outbound structural model — NOT the inbound reputation blend.'
structural_type:
type:
- string
- 'null'
enum:
- premium_prs
- satellite
- intl_network
- intl_premium
- freephone
- unallocated_dialable
- ordinary
description: The structural class the destination resolved to (null if no range matched).
reason_codes:
type: array
items:
type: string
description: Legible drivers, e.g. `structural:intl_premium` or `dynamic:<source_class>`.
example:
- structural:intl_premium
note:
type: string
cost_estimate:
type:
- object
- 'null'
description: Indicative RETAIL cost to reach `to`, from aggregated provider price-list decks (longest-prefix match, filtered by the destination's parsed line type). Returns BOTH channels regardless of `context` — `voice` priced per minute, `sms` per message — each null when no deck covers that channel; the whole object is null when neither does (fail-open). All amounts are decimal USD strings. Supplementary signal, never wholesale interconnect cost and never a quote.
properties:
currency:
type: string
enum:
- USD
note:
type: string
voice:
allOf:
- $ref: '#/components/schemas/ChannelCost'
description: Per-minute voice rate, or null if no voice deck covers the destination.
sms:
allOf:
- $ref: '#/components/schemas/ChannelCost'
description: Per-message SMS rate, or null if no SMS deck covers the destination.
enrolled:
type: boolean
provenance_recorded:
type: boolean
provenance_note:
type: string
edge_id:
type: string
ttl_seconds:
type: integer
example: 604800
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/RateLimited'
/api/v1/outbound/enroll:
post:
tags:
- Outbound
summary: Enroll a number for call-provenance
description: 'Owner-only control that enrolls (or, with `enrolled: false`, revokes) a verified business number you own for call-provenance. Enrollment is your attestation that this number only places calls after a Numbers Online outbound pre-call lookup, so the absence of a matching pre-call edge becomes a supplementary signal that a complaint may concern a spoofed call rather than one you placed. Only numbers bound to your own account can be enrolled; revocable and abuse-monitored. Requires an account-level API key with the `precall` use case (not a tenant sub-key). Supplementary signal only — not a compliance determination. This is the canonical spelling; the original /api/v1/precall/enroll stays a permanent alias served by the same handler. The same toggle is also available resource-shaped: GET /api/v1/account/phones lists your numbers, PATCH /api/v1/account/phones/{e164} flips enrollment.'
operationId: precallEnroll
security:
- ApiKeyAuth: []
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- number
properties:
number:
type: string
example: '+442071838750'
enrolled:
type: boolean
default: true
description: Set false to revoke enrollment.
responses:
'200':
description: Enrollment updated
content:
application/json:
schema:
type: object
properties:
schema_version:
type: string
example: '2026-06-14'
ok:
type: boolean
number:
type: string
enrolled:
type: boolean
attestation:
type: string
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: Tenant sub-keys cannot manage enrollment
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Number not bound to your account
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/v1/precall/lookup:
post:
tags:
- Outbound
summary: Outbound pre-call check — original spelling (permanent alias)
description: Permanent alias of POST /api/v1/outbound/lookup — the same handler under the original path, kept forever (it is baked into published guides and agent prompts already in the field). Request, response, auth, billing, and limits are identical; see the canonical entry. Prefer /api/v1/outbound/lookup in new integrations.
operationId: precallLookupAlias
deprecated: true
security:
- ApiKeyAuth: []
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- to
properties:
to:
type: string
example: '+14155551212'
from:
type: string
example: '+442071838750'
context:
type: string
enum:
- outbound_voice
- outbound_sms
responses:
'200':
description: Identical to POST /api/v1/outbound/lookup (same handler).
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/RateLimited'
/api/v1/precall/enroll:
post:
tags:
- Outbound
summary: Enroll a number — original spelling (permanent alias)
description: Permanent alias of POST /api/v1/outbound/enroll — the same handler under the original path, kept forever. Request, response, auth, and limits are identical; see the canonical entry. Prefer /api/v1/outbound/enroll (or the resource form PATCH /api/v1/account/phones/{e164}) in new integrations.
operationId: precallEnrollAlias
deprecated: true
security:
- ApiKeyAuth: []
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- number
properties:
number:
type: string
example: '+442071838750'
enrolled:
type: boolean
default: true
responses:
'200':
description: Identical to POST /api/v1/outbound/enroll (same handler).
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: Tenant sub-keys cannot manage enrollment
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Number not bound to your account
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
ChannelCost:
type: object
description: 'Indicative RETAIL cost to reach a destination on ONE channel, aggregated across provider price-list decks. All amounts are decimal USD strings (money is integer micro-USD internally). `unit` says whether the figures are per minute (voice) or per message (sms). The channel-relevant lane fields (voice: `international`/`local`; sms: `person`/`application`) appear at the aggregate level and per provider; `breakdown` lists only publicly-displayable providers (cheapest avg first), with anonymous sources folded into the aggregate numbers only.'
properties:
unit:
type: string
enum:
- per_minute
- per_message
min_usd:
type: string
example: '0.0072'
avg_usd:
type: string
example: '0.0146'
max_usd:
type: string
example: '0.32'
network_type:
type:
- string
- 'null'
description: The line-type filter that was applied (e.g. `mobile`, `premium`), or null when unfiltered.
example: mobile
providers:
type: integer
description: Distinct providers behind the aggregate (named + anonymous).
example: 3
rate_count:
type: integer
description: Underlying rate entries aggregated.
example: 5
international:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'Voice only: cross-provider international-lane aggregate.'
local:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'Voice only: cross-provider in-country-lane aggregate.'
person:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'SMS only: cross-provider P2P aggregate.'
application:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'SMS only: cross-provider A2P aggregate.'
breakdown:
type: array
items:
$ref: '#/components/schemas/ProviderCost'
Error:
type: object
properties:
success:
type: boolean
example: false
description: Legacy field emitted ONLY by the pre-v1 parse family (/api/parse, /api/parse/bulk, /api/countries). /v1 routes return only `error` — do not depend on `success` there.
error:
type: string
description: Human-readable error message (prose — switch on `code`, not on this string).
code:
type: string
enum:
- missing_key
- invalid_key
- use_case_forbidden
- rate_limited_key
- rate_limited_pool
- rate_limited_ip
- signature_invalid
- insufficient_balance
- account_suspended
- paid_verification_required
- receipt_invalid
description: 'Stable machine-readable error code (added 2026-06-12, additive — older errors may omit it). See the "Error codes" section in the API description for the full table. A valid key on the wrong use case returns 403 use_case_forbidden (not 401): re-authing will not fix a permissions problem.'
retry_after_seconds:
type: integer
description: 'Present on 429s: seconds until the window resets (mirrors the Retry-After header).'
required:
- error
ProviderCost:
type: object
description: 'One named, publicly-displayable provider''s slice of the aggregate. Anonymous sources never appear here — they stay inside the channel-level numbers only. The lane fields present depend on the channel: a voice cost carries `international`/`local`, an SMS cost carries `person`/`application`; each is null when that provider''s decks do not split that way.'
properties:
name:
type: string
example: DIDWW
domain:
type:
- string
- 'null'
example: didww.com
min_usd:
type: string
example: '0.0072'
avg_usd:
type: string
example: '0.0146'
max_usd:
type: string
example: '0.32'
international:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'Voice: international lanes (default + origin-based).'
local:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'Voice: in-country lanes.'
person:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'SMS: person-originated (P2P) decks.'
application:
allOf:
- $ref: '#/components/schemas/LaneUsd'
description: 'SMS: application-originated (A2P) decks.'
LaneUsd:
type: object
description: A min/avg/max spread for one pricing lane, as decimal USD strings.
properties:
min_usd:
type: string
example: '0.0072'
avg_usd:
type: string
example: '0.0146'
max_usd:
type: string
example: '0.32'
responses:
RateLimited:
description: Per-key rate limit exceeded. Retry after the number of seconds in the Retry-After header.
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: API key for authentication
BearerAuth:
type: http
scheme: bearer
description: Bearer token authentication
CidQueryKeyAuth:
type: apiKey
in: query
name: key
description: API key in the `?key=` query param. Accepted by the header-less PBX endpoint GET /api/v1/cid/{number} and by the webhook adapters POST /api/v1/integrations/retell/inbound, POST /api/v1/integrations/vapi/tool, and POST /api/v1/sbc/redirect, whose upstream platforms set only a static webhook URL and cannot send an Authorization/X-API-Key header. The key can leak into access logs — use a dedicated, rotated key, and prefer header auth wherever the client supports it.