Developer Platform

Temp Number API - Phone Number, SMS & OTP

Provision virtual phone numbers, receive SMS in real time and automate OTP verification - from a clean REST API, or straight from your AI assistant.

REST · JSON MCP for AI agents cURL · Node.js · Python · PHP
BASE URL https://api.temp-number.com/v1

Home / Developers

Introduction

The Temp Number API is organised around predictable resources, uses standard HTTP verbs and returns JSON for every response. One key gives read and write access to your account only - there is no cross-account access.

ProtocolHTTPS · JSON
AuthBearer key
Versionv1 (stable)
Endpoints17 live

Quickstart

From zero to a verification code. Pick your language once - every sample on the page follows it.

  1. Create an API key

    In Dashboard → API Access, create a key. It is shown once: store it as the environment variable TEMP_NUMBER_API_KEY.

  2. Check your balance

    Every call carries your key in the Authorization header. Start with something harmless: your wallet balance.

    curl --request GET "https://api.temp-number.com/v1/account" \
      --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
      --header "Accept: application/json"
  3. Pick a country and check stock

    GET /pricing lists every country with the exact price per duration. Then confirm stock for the one you want.

    curl --request GET "https://api.temp-number.com/v1/inventory?country_code=US&duration=oneMonth" \
      --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
      --header "Accept: application/json"
  4. Buy a number

    Send a fresh Idempotency-Key with every purchase. If the network drops, retry with the same key: you are never charged twice. Keep data.id from the response.

    curl --request POST "https://api.temp-number.com/v1/numbers/buy" \
      --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
      --header "Accept: application/json" \
      --header "Content-Type: application/json" \
      --header "Idempotency-Key: $(uuidgen)" \
      --data '{"country_code":"US","duration":"oneMonth"}'
  5. Read the SMS

    Poll the number every few seconds until the message arrives. You get the raw text; pull the code out on your side.

    curl --request GET "https://api.temp-number.com/v1/numbers/1482/sms" \
      --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
      --header "Accept: application/json"
  6. No SMS? Cancel for a refund

    Between 15 and 120 minutes after purchase, a number that has received no SMS can be cancelled for a full refund to your wallet.

    curl --request POST "https://api.temp-number.com/v1/numbers/1482/cancel" \
      --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
      --header "Accept: application/json" \
      --header "Idempotency-Key: $(uuidgen)"

Authentication

Send your API key in the standard Authorization header on every request:

http
Authorization: Bearer YOUR_API_KEY
  • Create keys under API Access. A key is shown in full once, at creation.
  • Use one key per integration, so you can revoke one without breaking the others. Changing your password revokes every key.
  • A missing, malformed or revoked key answers 401. An account that has not verified its email, or is suspended, answers 403 with code email_unverified or account_suspended.
  • REST keys work on /v1 only. The MCP server uses its own sign-in or MCP keys - see MCP server.
Treat your key like a password. Keep it on your server, never in a web page or mobile app. Rotate it at once if it was ever logged, committed or shared.

Base URL & versioning

All endpoints live under the permanent /v1 prefix:

base url
https://api.temp-number.com/v1

Additive changes - new fields, endpoints or optional parameters - ship without a version bump, so ignore fields you do not know. A breaking change would ship under /v2; your integration is never moved silently. The machine-readable contract is the OpenAPI spec.

Requests & responses

  • Send JSON bodies with Content-Type: application/json, and Accept: application/json on every request.
  • Success: { "success": true, "data": …, "meta": {…} }. meta carries counts, pagination and money summaries.
  • Errors: { "success": false, "message": "…", "code": "…" }. Branch on code; the message is for people. See Errors.
  • Times are ISO 8601. Money is USD with two decimals - always the price you pay.
  • Some objects still carry older field names for existing integrations (marked deprecated in the reference). New code should ignore them.
Responses never name an upstream provider or show wholesale cost. The only price you see is the one charged to your wallet - safe to show to your own users.

Errors

Errors use standard HTTP status codes. Business errors carry a stable, machine-readable code; some carry details with more context.

422 · business error
{
  "success": false,
  "message": "Insufficient wallet balance.",
  "code": "insufficient_funds"
}
422 · validation error
{
  "message": "The country code field is required.",
  "errors": {
    "country_code": ["The country code field is required."]
  }
}
StatuscodeMeaningWhat to do
400 - The Idempotency-Key header is missing or malformed on buy, renew or cancel. Body: {"error": "Missing Idempotency-Key header."} Send a UUID in Idempotency-Key.
401 - No bearer token, a malformed one, or a revoked key. Check the Authorization header; create a new key if it was revoked.
403 email_unverified The account has not verified its email address yet. Verify the email on the website, then retry.
403 account_suspended The account is suspended. Contact support.
403 kyc_required The account must verify its identity before buying new numbers. Nothing was charged; renewals still work. Complete identity verification in Account settings, then retry.
403 - The key has no API access (an MCP key, for example), or GET /receive-sms was asked about a number on another account. Use a REST API key; only query your own numbers.
404 - The number, message or country is not on your account or not on sale right now. Ids of other accounts also answer 404. Re-list with GET /numbers or GET /pricing.
404 private_email_closed The private inbox is closed because its number was cancelled or refunded. Nothing to retry.
409 - The Idempotency-Key was already used with a different body, or the first request with it is still running. Use a new key for a new request; wait and retry for the in-flight one.
422 insufficient_funds The wallet cannot cover the price. Top up the wallet and retry.
422 out_of_stock The market sold out between your price check and the purchase. Re-check GET /inventory and pick another duration or type.
422 invalid_duration The duration is not sold for that country and number type. Re-fetch GET /pricing/{countryCode}.
422 - Validation failed (an `errors` object names each field) or a rule refused the action, such as cancelling outside the refund window. Read `message`; fix the request rather than retrying it.
429 - Rate limit reached. Wait for the Retry-After header, then retry with backoff.
503 purchases_paused New purchases are paused platform-wide. Nothing was charged; renewals and cancels still work. Retry after Retry-After (60 s).
5xx - Something failed on our side. Retry with the SAME Idempotency-Key - you will never be charged twice.

Rate limits

Limits apply per key, and money-moving calls have a tighter limit per account on top:

Applies toLimitCounted
Every endpoint60 / minuteper API key
Buy, renew, cancel10 / minuteper account, on top of the above
GET /receive-sms60 / minuteper API key
GET /receive-sms30 / minuteper API key and phone number
GET /receive-sms30 / minuteper IP address

Over a limit you get 429 with a Retry-After header (seconds). Wait at least that long, then back off with jitter. Responses also carry X-RateLimit-Limit and X-RateLimit-Remaining.

Waiting for an SMS? Poll one number every 5 seconds at most.

Idempotency

POST /numbers/buy, /renew and /cancel require an Idempotency-Key header: a UUID you create for each new action. If the network drops, send the same request with the same key - you get the original response back (marked X-Idempotency-Replayed: true) and are never charged twice.

  • Missing key: 400. Keys are up to 128 characters of letters, digits, - _ . :
  • Same key with a different body: 409. Same key while the first request is still running: 409 - wait a moment and retry.
  • Keys are remembered per account and endpoint. Never reuse one for a different purchase.

Pagination

GET /receive-sms uses cursor pagination, newest first. Read meta.hasMore; while it is true, send meta.nextCursor back as cursor with the same phoneNumber. Cursors are opaque - do not build or edit them.

next page
GET https://api.temp-number.com/v1/receive-sms?phoneNumber=%2B14035551234&limit=50&cursor=eyJkYXRlIjoiMjAyNi0wNS0xMyJ9

Lists that are small by nature (/numbers, /pricing) return everything in one response; the private inbox uses page and per_page.

Acceptable use

The API is built for developers: verification and OTP flows, automated onboarding, QA and system testing, and any product that needs inbound SMS or phone numbers at scale.

  • Use it for legitimate verification, development and testing only.
  • No scams, spam, fraud or deception. Numbers used for scams or other abusive activity get the account banned immediately and without notice, associated numbers cancelled, and no refund is due.
  • Not for regulated or identity-bound accounts. Our shared, temporary numbers do not support SMS verification for banking, payment, cryptocurrency, government or other services that verify your legal identity (KYC/AML) - use your own personal mobile number for those.
We monitor for abuse. Full conditions are in our Terms of Service.

API reference

Every live /v1 endpoint. Requests are on the right in your language; responses are real shapes.

Account

Read your wallet balance and settlement currency.

GET /v1/account

Get account balance

Returns the available wallet balance and its currency. Check it before a purchase so you can stop early with a clear "top up" message.

Response fields
  • balancenumber

    Available wallet balance.

  • currencystring

    Settlement currency, USD.

Errors
  • 401Missing or invalid key.
curl --request GET "https://api.temp-number.com/v1/account" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "balance": 124.50,
  "currency": "USD"
}

Pricing & stock

Browse live markets, the exact price you pay per duration, and real-time stock.

The country pricing object

One country and every package you can buy there. price is the final amount your wallet is charged; upstream cost is never exposed.

Fields
  • country_codestring

    ISO 3166-1 alpha-2 code, e.g. US.

  • country_namestring

    Display name.

  • packages[].number_typeenum

    voip or non_voip.

  • packages[].durationenum

    The key you send to buy: halfDay, oneDay, oneWeek, oneMonth, threeMonths, sixMonths or oneYear. Each country offers its own set.

  • packages[].duration_labelstring

    Human label, e.g. "1 Month".

  • packages[].pricenumber

    Final price in USD.

  • packages[].original_pricenumber | null

    The price before a discount, when one applies.

  • packages[].updated_atnull

    Reserved.

object
{
  "country_code": "US",
  "country_name": "United States",
  "packages": [
    {
      "number_type": "voip",
      "duration": "oneMonth",
      "duration_label": "1 Month",
      "price": 4.99,
      "original_price": 5.99,
      "updated_at": null
    }
  ]
}
GET /v1/pricing

List pricing for all countries

The complete live catalog: a list of country pricing objects. The price field is the final user-facing rate in USD, exactly what your wallet is charged.

Returns

A list of country pricing objects in data, with meta.count.

Errors
  • 401Missing or invalid key.
curl --request GET "https://api.temp-number.com/v1/pricing" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": [
    {
      "country_code": "US",
      "country_name": "United States",
      "packages": [
        {
          "number_type": "voip",
          "duration": "oneMonth",
          "duration_label": "1 Month",
          "price": 4.99,
          "original_price": 5.99,
          "updated_at": null
        },
        {
          "number_type": "voip",
          "duration": "threeMonths",
          "duration_label": "3 Months",
          "price": 12.99,
          "original_price": null,
          "updated_at": null
        }
      ]
    }
  ],
  "meta": { "count": 1 }
}
GET /v1/pricing/{countryCode}

Get pricing for one country

Live pricing for a single country. A 404 means the country has no active route right now: treat it as a temporary "out of stock", not as "never sold".

Parameters
  • countryCode string path Required

    ISO 3166-1 alpha-2 country code, e.g. US, GB, CA.

Returns

One country pricing object in data.

Errors
  • 404No active route for that country right now.
curl --request GET "https://api.temp-number.com/v1/pricing/GB" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": {
    "country_code": "GB",
    "country_name": "United Kingdom",
    "packages": [
      {
        "number_type": "voip",
        "duration": "oneMonth",
        "duration_label": "1 Month",
        "price": 5.49,
        "original_price": null,
        "updated_at": null
      }
    ]
  }
}
GET /v1/inventory

Check stock

Real-time stock for a country and duration, so you can disable a Buy button before anyone clicks it. Leave number_type out to get every type side by side in types[]. A green result is a strong signal, but the purchase itself is the final check.

Parameters
  • country_code string query Required

    ISO 3166-1 alpha-2 country code.

  • duration enum query Optional

    A duration key from GET /pricing/{countryCode}. Omit for the route default.

    One of halfDayoneDayoneWeekoneMonththreeMonthssixMonthsoneYear
  • number_type enum query Optional

    Pin the check to one type. Omit to see all types.

    One of voipnon_voip
Response fields
  • stockinteger | null

    Numbers available (summed over types[] when number_type is omitted).

  • availableboolean

    True when at least one number can be bought.

  • statusstring

    Human label: In Stock, Out of Stock, Coming Soon…

  • types[]array

    Only when number_type is omitted: number_type, stock, available, status per type the market sells.

Errors
  • 422Nothing on sale for that selection; details carries the stock fields.
curl --request GET "https://api.temp-number.com/v1/inventory?country_code=US&duration=oneMonth" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": {
    "country_code": "US",
    "duration": "oneMonth",
    "number_type": null,
    "stock": 47,
    "available": true,
    "status": "In Stock",
    "types": [
      { "number_type": "voip", "stock": 0, "available": false, "status": "Out of Stock" },
      { "number_type": "non_voip", "stock": 47, "available": true, "status": "In Stock" }
    ]
  }
}

Numbers

List, buy, renew and cancel your private numbers. Money-moving calls are idempotent and replay-safe.

The number object

A number on your account. Buy, renew and cancel also return a money summary in meta: charged (or refunded), currency and wallet_balance_after.

Fields
  • idinteger

    Use it in /numbers/{id}/… calls. Webhooks send it as number.rentalId.

  • phone_numberstring

    E.164, e.g. +14035551234.

  • country_codestring | null

    ISO 3166-1 alpha-2.

  • country_namestring

    Display name.

  • number_typeenum

    voip or non_voip.

  • statusenum

    active, expired, cancelled or refunded.

  • purchase_datestring

    ISO 8601, UTC.

  • expire_datestring

    ISO 8601, UTC. After it the number stops receiving SMS and cannot be renewed.

Deprecated aliases still sent for older integrations: country, provider, purchased_at and expires_at (epoch milliseconds). Do not build on them.

object
{
  "id": 1482,
  "phone_number": "+14035551234",
  "country_code": "US",
  "country_name": "United States",
  "number_type": "voip",
  "status": "active",
  "purchase_date": "2026-05-13T17:42:09Z",
  "expire_date": "2026-06-13T17:42:09Z"
}
GET /v1/numbers

List your numbers

Every number on your account, newest first. Use status=expiring_soon (active numbers within 10 days of expiry) to drive renewal reminders.

Parameters
  • status enum query Optional

    Lifecycle filter. Default all.

    One of allactiveexpiring_soonexpiredcancelled
Returns

A list of number objects in data; meta.count and meta.status.

Errors
  • 401Missing or invalid key.
curl --request GET "https://api.temp-number.com/v1/numbers?status=expiring_soon" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": [
    {
      "id": 1482,
      "phone_number": "+14035551234",
      "country_code": "US",
      "country_name": "United States",
      "number_type": "voip",
      "status": "active",
      "purchase_date": "2026-05-13T17:42:09Z",
      "expire_date": "2026-06-13T17:42:09Z"
    }
  ],
  "meta": { "count": 1, "status": "expiring_soon" }
}
POST /v1/numbers/buy Idempotency-Key required

Buy a number

Allocates a new private number and charges your wallet in one atomic step. If fulfilment fails after the charge, the charge is reversed: you never end up paid-but-empty. The response shows the amount charged and your balance after.

Parameters
  • country_code string body Required

    A country from GET /pricing.

  • duration enum body Required

    A duration key from that country's packages[] - each country offers its own set.

    One of halfDayoneDayoneWeekoneMonththreeMonthssixMonthsoneYear
  • number_type enum body Optional

    Omit to use the route default.

    One of voipnon_voip
  • Idempotency-Key string header Required

    A UUID, new for each purchase, reused on retries.

Returns

The new number object in data; meta.charged, meta.currency, meta.wallet_balance_after.

Errors
  • 422insufficient_fundsThe wallet cannot cover the price.
  • 422out_of_stockSold out since your stock check.
  • 422invalid_durationDuration not sold for this country/type.
  • 403kyc_requiredIdentity verification needed first. Nothing charged.
  • 503purchases_pausedPurchases paused platform-wide. Retry-After: 60.
curl --request POST "https://api.temp-number.com/v1/numbers/buy" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{"country_code":"US","duration":"oneMonth","number_type":"voip"}'
Response
{
  "success": true,
  "data": {
    "id": 1482,
    "phone_number": "+14035551234",
    "country_code": "US",
    "country_name": "United States",
    "number_type": "voip",
    "status": "active",
    "purchase_date": "2026-05-13T17:42:09Z",
    "expire_date": "2026-06-13T17:42:09Z"
  },
  "meta": {
    "charged": 4.99,
    "currency": "USD",
    "wallet_balance_after": 119.51
  }
}
POST /v1/numbers/{id}/renew Idempotency-Key required

Renew a number

Extends a number by the chosen package. The new expiry is added to the current one, so renewing early loses nothing. The price is the current site rate, worked out on our side. Expired numbers cannot be renewed: buy a new one.

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • package enum body Optional

    Renewal length. Default oneMonth. (duration is accepted as a legacy alias.)

    One of oneMonththreeMonthssixMonthsoneYear
  • Idempotency-Key string header Required

    A UUID, new for each renewal, reused on retries.

Returns

The updated number object in data; meta.charged, meta.currency, meta.wallet_balance_after.

Errors
  • 404Not a number on your account.
  • 422insufficient_fundsThe wallet cannot cover the renewal.
  • 422Expired, cancelled, or not renewable.
curl --request POST "https://api.temp-number.com/v1/numbers/1482/renew" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: $(uuidgen)" \
  --data '{"package":"threeMonths"}'
Response
{
  "success": true,
  "data": {
    "id": 1482,
    "phone_number": "+14035551234",
    "country_code": "US",
    "country_name": "United States",
    "number_type": "voip",
    "status": "active",
    "purchase_date": "2026-05-13T17:42:09Z",
    "expire_date": "2026-09-13T17:42:09Z"
  },
  "meta": {
    "charged": 12.99,
    "currency": "USD",
    "wallet_balance_after": 106.52
  }
}
POST /v1/numbers/{id}/cancel Idempotency-Key required

Cancel a number for a refund

Releases a number and refunds its full price to your wallet. Allowed only between 15 and 120 minutes after purchase, while the number is active and before it has received any SMS. Outside those rules the call is refused with 422 and nothing changes; too early, details.seconds_until_open says how long to wait.

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • Idempotency-Key string header Required

    A UUID, reused on retries.

Returns

The number object (status cancelled) in data; meta.refunded, meta.currency, meta.wallet_balance_after.

Errors
  • 404Not a number on your account.
  • 422Too early (under 15 min), too late (over 120 min), already received SMS, or not active.
curl --request POST "https://api.temp-number.com/v1/numbers/1482/cancel" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json" \
  --header "Idempotency-Key: $(uuidgen)"
Response
{
  "success": true,
  "data": {
    "id": 1482,
    "phone_number": "+14035551234",
    "country_code": "US",
    "country_name": "United States",
    "number_type": "voip",
    "status": "cancelled",
    "purchase_date": "2026-05-13T17:42:09Z",
    "expire_date": "2026-06-13T17:42:09Z"
  },
  "meta": {
    "refunded": 4.99,
    "currency": "USD",
    "wallet_balance_after": 124.50
  }
}

SMS

Read the SMS your numbers receive. You get the raw text; extracting a code is up to you.

The SMS message object

One received SMS, as returned by GET /numbers/{id}/sms. (GET /receive-sms uses a camelCase variant, shown on that endpoint.)

Fields
  • idinteger

    Message id.

  • fromstring

    Sender: a phone number or a name such as WhatsApp.

  • bodystring

    The full message text.

  • received_atstring

    ISO 8601, UTC.

Deprecated aliases still sent: sender (= from) and message (= body).

object
{
  "id": 9912,
  "from": "+18005550199",
  "body": "Your code is 432901",
  "received_at": "2026-05-13T17:43:14Z"
}
GET /v1/numbers/{id}/sms

Get a number and its SMS

One number together with every SMS it has received, newest first. Best for a "drill into this number" view, and for polling a single number while you wait for a code. Ids that are not yours answer 404.

Parameters
  • id integer path Required

    Number id from GET /numbers.

Returns

data.number (a number object) and data.messages (SMS message objects); meta.count.

Errors
  • 404Not a number on your account.
curl --request GET "https://api.temp-number.com/v1/numbers/1482/sms" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": {
    "number": {
      "id": 1482,
      "phone_number": "+14035551234",
      "country_code": "US",
      "country_name": "United States",
      "number_type": "voip",
      "status": "active",
      "purchase_date": "2026-05-13T17:42:09Z",
      "expire_date": "2026-06-13T17:42:09Z"
    },
    "messages": [
      {
        "id": 9912,
        "from": "+18005550199",
        "body": "Your code is 432901",
        "received_at": "2026-05-13T17:43:14Z"
      }
    ]
  },
  "meta": { "count": 1 }
}
GET /v1/receive-sms

Receive SMS by phone number

Cursor-paginated SMS for a number you own, addressed by its phone number instead of its id. Best for high-volume inboxes. A number that is not yours answers 403.

Parameters
  • phoneNumber string query Required

    E.164 or digits only, e.g. +14035551234. URL-encode the +.

  • limit integer query Optional

    Page size 1-100. Default 50.

  • cursor string query Optional

    meta.nextCursor from the previous page.

Response fields
  • data.messages[].idinteger

    Message id.

  • data.messages[].fromstring

    Sender.

  • data.messages[].tostring

    Your number.

  • data.messages[].messagestring

    The full text.

  • data.messages[].receivedAtstring

    ISO 8601.

  • meta.hasMore / nextCursorboolean / string | null

    Pass nextCursor as cursor for the next page.

Errors
  • 403The number is not on your account.
  • 422phoneNumber missing or invalid.
curl --request GET "https://api.temp-number.com/v1/receive-sms?phoneNumber=%2B14035551234&limit=50" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": {
    "phoneNumber": "+14035551234",
    "purchaseStatus": "active",
    "messages": [
      {
        "id": 1842,
        "from": "WhatsApp",
        "to": "+14035551234",
        "message": "Your verification code is 483920.",
        "receivedAt": "2026-05-13T17:43:14+00:00"
      }
    ]
  },
  "meta": {
    "count": 1,
    "pageSize": 50,
    "hasMore": false,
    "nextCursor": null,
    "prevCursor": null,
    "requestedAt": "2026-05-13T17:43:20+00:00"
  }
}

Private email

A free, receive-only [email protected] inbox for each private number. One address per number.

The private email object

The inbox attached to one number.

Fields
  • addressstring | null

    The address, or null before it is claimed.

  • statusenum

    active or none.

  • unread_countinteger

    Unread emails.

  • created_atstring | null

    When the address was claimed.

  • eligibleboolean

    True when the number can claim an address now.

  • linked_numberstring

    The phone number it belongs to.

  • domainstring

    privnum.com

  • suggestionsstring[]

    Name ideas while eligible.

object
{
  "address": "[email protected]",
  "status": "active",
  "unread_count": 2,
  "created_at": "2026-07-03T10:15:00+00:00",
  "eligible": false,
  "linked_number": "+14035551234",
  "domain": "privnum.com",
  "suggestions": []
}
GET /v1/numbers/{id}/private-email

Get the private email

The address of a number, or, before one is claimed, whether it can have one and some name suggestions.

Parameters
  • id integer path Required

    Number id from GET /numbers.

Returns

A private email object in data.

Errors
  • 404Not a number on your account.
curl --request GET "https://api.temp-number.com/v1/numbers/1482/private-email" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": {
    "address": null,
    "status": "none",
    "unread_count": 0,
    "created_at": null,
    "eligible": true,
    "linked_number": "+14035551234",
    "domain": "privnum.com",
    "suggestions": ["alex.smith", "alex.smith42"]
  }
}
GET /v1/numbers/{id}/private-email/availability

Check a name

Live check that a name is valid and free before you claim it.

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • local_part string query Required

    The part before @: 3-64 letters, numbers, dots, dashes or underscores.

Response fields
  • availableboolean

    True when you can claim it.

  • reasonenum | null

    too_short, too_long, invalid, reserved or taken.

Errors
  • 404Not a number on your account.
curl --request GET "https://api.temp-number.com/v1/numbers/1482/private-email/availability?local_part=alex.smith" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": {
    "local_part": "alex.smith",
    "address": "[email protected]",
    "available": true,
    "reason": null,
    "message": "Available"
  }
}
POST /v1/numbers/{id}/private-email

Claim an address

Gives an active number its address. Free, one per number, and a claimed address is never handed to anyone else.

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • local_part string body Required

    The name you checked.

Returns

The private email object in data.

Errors
  • 404Not your number, or no address yet.
  • 422Name taken or invalid (details.reason), the number already has an address, or it is not active.
curl --request POST "https://api.temp-number.com/v1/numbers/1482/private-email" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data '{"local_part":"alex.smith"}'
Response
{
  "success": true,
  "data": {
    "address": "[email protected]",
    "status": "active",
    "unread_count": 0,
    "created_at": "2026-07-03T10:15:00+00:00",
    "eligible": false,
    "linked_number": "+14035551234",
    "domain": "privnum.com",
    "suggestions": []
  }
}
GET /v1/numbers/{id}/private-email/inbox

List emails

Email summaries, newest first, with page-based pagination and the unread count at the top level.

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • page integer query Optional

    Page number. Default 1.

  • per_page integer query Optional

    1-100. Default 25.

Response fields
  • data[]array

    id, from_email, from_name, subject, preview, attachment_count, is_read, received_at.

  • metaobject

    current_page, per_page, total, last_page.

  • unread_countinteger

    Top level, next to data.

Errors
  • 404Not your number, or no address yet.
  • 404private_email_closedThe number was cancelled or refunded.
curl --request GET "https://api.temp-number.com/v1/numbers/1482/private-email/inbox?page=1&per_page=25" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": [
    {
      "id": 77,
      "from_email": "[email protected]",
      "from_name": "Example Billing",
      "subject": "Your receipt",
      "preview": "Thanks for your order…",
      "attachment_count": 1,
      "is_read": false,
      "received_at": "2026-07-04T08:01:12+00:00"
    }
  ],
  "meta": { "current_page": 1, "per_page": 25, "total": 1, "last_page": 1 },
  "unread_count": 1
}
GET /v1/numbers/{id}/private-email/inbox/{messageId}/body

Read an email

The full email (HTML and text) with its attachment list. Reading it marks it read.

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • messageId integer path Required

    Email id from the inbox list.

Errors
  • 404Not your number, or no address yet.
  • 404private_email_closedThe number was cancelled or refunded.
curl --request GET "https://api.temp-number.com/v1/numbers/1482/private-email/inbox/77/body" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": {
    "id": 77,
    "subject": "Your receipt",
    "from_email": "[email protected]",
    "from_name": "Example Billing",
    "received_at": "2026-07-04T08:01:12+00:00",
    "html": "<p>Thanks for your order…</p>",
    "text": "Thanks for your order…",
    "attachments": [
      { "index": 0, "filename": "receipt.pdf", "content_type": "application/pdf", "size_bytes": 48211 }
    ]
  }
}
GET /v1/numbers/{id}/private-email/inbox/{messageId}/attachments/{index}

Download an attachment

The raw file, with its own Content-Type and filename. index comes from the email's attachments[].

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • messageId integer path Required

    Email id from the inbox list.

  • index integer path Required

    attachments[].index from Read an email.

Errors
  • 404Not your number, or no address yet.
  • 404private_email_closedThe number was cancelled or refunded.
  • 404No attachment at that index.
curl --request GET "https://api.temp-number.com/v1/numbers/1482/private-email/inbox/77/attachments/0" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --output receipt.pdf
Response
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename=receipt.pdf

<binary file>
DELETE /v1/numbers/{id}/private-email/inbox/{messageId}

Delete an email

Deletes the email and its attachments.

Parameters
  • id integer path Required

    Number id from GET /numbers.

  • messageId integer path Required

    Email id from the inbox list.

Errors
  • 404Not your number, or no address yet.
  • 404private_email_closedThe number was cancelled or refunded.
curl --request DELETE "https://api.temp-number.com/v1/numbers/1482/private-email/inbox/77" \
  --header "Authorization: Bearer $TEMP_NUMBER_API_KEY" \
  --header "Accept: application/json"
Response
{
  "success": true,
  "data": null
}

AI & MCP

Let an AI assistant use your account directly, or hand a coding agent everything it needs to build on the API.

MCP server

Alongside the REST API we run a Model Context Protocol server. Connect it to Claude, ChatGPT or your editor and the assistant can check prices and stock, buy a number and read its SMS as native tools, with no code on your side.

Endpoint
MCP endpoint · streamable HTTP
https://mcp.temp-number.com/mcp

How sign-in works

  • Add the URL to your AI app. It sends you to Temp Number to sign in with your own account (OAuth 2.1 with PKCE) - no key to copy.
  • On the consent screen you choose what the assistant may do. Reading balance, prices, your numbers and their SMS is always included. Allow purchases - buying, renewing and cancelling with your wallet - is off unless you tick it.
  • Every connection is listed under Dashboard → MCP Server → Connected apps, where you can disconnect it at any time.
  • Prefer a key (CI, scripts, headless agents)? Create an MCP key on the same page and send it as Authorization: Bearer <key>. MCP keys and REST API keys are not interchangeable.

Tools

ToolWhat it doesNeeds "Allow purchases"
get-balanceRetrieve the authenticated account wallet balance and billing currency.No
get-pricingReturn the active pricing catalog for all countries or a single ISO country code.No
get-inventoryInspect live inventory availability for a country, duration, and optional number type.No
purchase-numberPurchase one private number for the authenticated account using wallet funds.Yes
list-active-numbersReturn numbers owned by the authenticated account, optionally filtered by status.No
check-smsFetch SMS messages for a purchased number ID owned by the authenticated account.No
renew-numberRenew a purchased number by ID using an optional duration package.Yes
cancel-numberCancel a purchased number by ID and return any eligible wallet refund.Yes

Connect your AI tool

Pick your app. Most connect in one click: you sign in to Temp Number, tick what the assistant may do, and click Allow.

Claude - claude.ai, Desktop and mobile

Nothing to install. Works in the browser, the desktop app and on mobile.

  1. Click Add to Claude. Claude opens with Temp Number and its URL already filled in. Click Add.
  2. Click Connect, sign in to Temp Number, choose whether Claude may make purchases, and click Allow.
  3. In any chat, open the + menu → Connectors and switch Temp Number on. Try: "What does a US number cost for a month?"
Team and Enterprise: an owner adds the connector once under Organization settings → Connectors. Each member then signs in with their own Temp Number account, so everyone spends from their own wallet.

No button? In Claude go to Customize → Connectors → Add custom connector and paste the MCP endpoint. Organisations with Claude's request-header option can send an MCP key as Authorization: Bearer <key> instead of signing in.

llms.txt & agent prompt

Building with an AI coding assistant? Give it the context below and it can integrate the API correctly in one go - auth, idempotency and rate limits included.

llms.txt OpenAPI spec
prompt.md
You are integrating the Temp Number REST API. Read the full, machine-readable reference before writing any code:

- API reference (llms.txt): https://temp-number.com/developers/llms.txt
- OpenAPI spec: https://api.temp-number.com/api.json
- Base URL: https://api.temp-number.com/v1
- MCP server (for AI agents that call tools directly): https://mcp.temp-number.com/mcp - sign in with OAuth, or send an MCP key as a Bearer token.

Rules: authenticate every request with `Authorization: Bearer <YOUR_API_KEY>` (created in the dashboard → API Access) and keep the key in an environment variable, never in code; send and accept JSON over HTTPS; send a unique `Idempotency-Key` (UUID) header on every POST that buys, renews or cancels - it is required, and retries must reuse the same key; branch on the `code` field of error responses, not on the message text; on HTTP 429 or 503, wait for the `Retry-After` header. All prices are final USD.

Task: <describe what you want to build - e.g. "buy a US number, wait for the verification SMS, then cancel it if it never arrives">.
https://temp-number.com/developers/llms.txt is an always-current, plain-text summary of every endpoint; the OpenAPI spec is the typed contract. Both update with the API.

Resources

What changed, and where to get help.

Changelog

Additive changes only - nothing on /v1 has been removed.

  • NewMCP: sign in with your Temp Number account

    One-click "Add to Claude", plus ChatGPT, Claude Code, Codex, Cursor and VS Code without copying a key. You choose on the consent screen whether the AI may spend.

  • DocsA new home for the docs

    Quickstart, search, and every sample in cURL, Node.js, Python and PHP. Errors, rate limits and refunds now match the live API exactly.

  • Newllms.txt for the whole site

    A plain-text map of the service for AI assistants, linking to the agent-ready API reference.

  • NewConnect your AI tool

    Setup for Claude Code, Codex, Cursor and VS Code, with one-click install links for Cursor and VS Code.

  • NewOpenAPI spec

    The machine-readable contract is served at https://api.temp-number.com/api.json.

  • ImprovedObjects match the reference

    Numbers and SMS use ISO 8601 dates and from/body. Errors carry a machine-readable code (out_of_stock, insufficient_funds, invalid_duration). /inventory reports every number type when you leave number_type out. The old field names still work as deprecated aliases.

  • NewPrivate email endpoints

    Claim a free [email protected] inbox for a number, read mail and download attachments over the API.

  • NewMCP server

    AI agents can check prices, buy numbers and read SMS as tools, using an MCP key.

  • LaunchPublic REST API v1

    Account, pricing, inventory, the number lifecycle and SMS, with Bearer-token auth.

Support

Stuck, or found something in these docs that does not match what the API does? Contact us - include the endpoint, the time of the request and the response body (never your API key).

Ready to build?

Create a key and make your first call in under a minute.

Sign in to start