All systems operational | daisysim.com/api/v1/server7
US Virtual Numbers ยท SMS Verification

DaisySim API

Programmatic access to US virtual phone numbers for SMS verification. Purchase numbers, poll for OTP codes, and manage activations โ€” all via a simple REST interface with wallet-based billing.

BASE URL https://daisysim.com/api/v1/server7

Authentication

๐Ÿ”‘

Bearer Token Required

Every request must include your API key as a Bearer token in the Authorization header. Keys are issued per account from your API Keys page and tied to your wallet balance.

HTTP Header
Authorization: Bearer YOUR_API_KEY
cURL Example
curl -X GET \
  https://daisysim.com/api/v1/server7/balance \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Rate Limits

Rate limits are enforced per account per minute. Exceeding a limit returns HTTP 429 with code RATE_LIMITED and the exact number of seconds to wait before retrying.

EndpointLimitWindow
POST /purchase 10 requests per 60 seconds
GET /check/{id} 60 requests per 60 seconds
POST /cancel/{id} 10 requests per 60 seconds
Retry hint included. The error message tells you exactly how long to wait โ€” e.g. "Too many purchase attempts. Please wait 43s."

Error Codes

All responses share a consistent envelope. On error, success is false and both error (human-readable) and code (machine-readable) fields are present.

HTTPcodeDescription
401UNAUTHENTICATEDMissing or invalid API key
403ACCOUNT_SUSPENDEDAccount has been blocked
403ACCOUNT_LOCKEDAccount is temporarily locked
404NOT_FOUNDActivation ID not found or does not belong to your account
409CONCURRENT_ERRORConcurrent transaction detected โ€” retry immediately
422PRICE_VERIFICATION_FAILEDService price could not be verified โ€” re-fetch /apps and retry
422CODE_RECEIVEDCannot cancel โ€” SMS code already arrived. The data object contains the code.
422TOO_EARLYMust wait 3 minutes from purchase before cancelling. Error message includes exact second(s) remaining.
422INVALID_COUNTRYCountry not supported โ€” use USA
400INSUFFICIENT_BALANCEWallet balance too low for this purchase
429RATE_LIMITEDToo many requests โ€” wait the number of seconds in the error message before retrying
503OUT_OF_STOCKNo numbers available from the selected pool right now โ€” try again shortly, or omit provider_id to use the cheapest available pool
502PROVIDER_ERRORUpstream error โ€” retry in a few seconds
500SERVER_ERRORInternal server error
Error Response Shape
{
  "success": false,
  "error":   "Human-readable error message",
  "code":    "MACHINE_READABLE_CODE"
}

GET

Balance

https://daisysim.com/api/v1/server7/balance

Returns your current wallet balance in USD along with your account email.

balancefloatCurrent wallet balance in USD (4 decimal places)
currencystringAlways USD
emailstringAuthenticated account email
200 OK
{
  "success": true,
  "message": "OK",
  "data": {
    "balance":  14.2500,
    "currency": "USD",
    "email":    "[email protected]"
  }
}
GET

Countries

https://daisysim.com/api/v1/server7/countries

Returns the list of supported countries. Currently USA only. Use the returned id when calling /apps/{country} and /purchase.

200 OK
{
  "success": true,
  "message": "OK",
  "data": {
    "countries": [
      { "id": "USA", "name": "USA" }
    ]
  }
}
GET

Apps

https://daisysim.com/api/v1/server7/apps/{country}

Returns all available services for a country with your final wallet price. Services prefixed sb_ support multiple pools and include a providers array sorted cheapest-first โ€” pass a provider_id to /purchase to use that exact pool, or omit it to use the cheapest available. Single-provider services return an empty providers array and the provider_id field is ignored on purchase.

Caching. The underlying service catalogue is cached for up to 30 minutes. Your per-account price view is cached for 5 minutes. If a service disappears between fetching apps and purchasing, the purchase is rejected with PRICE_VERIFICATION_FAILED โ€” re-fetch /apps and retry.
Provider selection is optional for sb_* services. When a provider_id is supplied, that exact pool is used โ€” if it has no stock the purchase is rejected with OUT_OF_STOCK, no fallback occurs. Omit provider_id to use the cheapest available pool.
ParamTypeRequiredDescription
country string required Country ID from /countries โ€” e.g. USA
codestringService identifier โ€” pass as app in /purchase
namestringHuman-readable service name
pricefloatPrice charged to your wallet in USD (4 decimal places). For multi-pool sb_* services this reflects the cheapest pool in the providers array.
providersarrayAlways present. Empty array [] for single-provider services. Non-empty for sb_* services, sorted cheapest-first.
provider_idstringOpaque pool identifier โ€” pass to /purchase as provider_id to attempt this pool first
pricefloatPrice for this specific pool including platform markup
countintegerApproximate number of available numbers from this pool right now
GET /apps/USA โ€” 200 OK
{
  "success": true,
  "message": "OK",
  "data": [

    // โ”€โ”€ Single-provider services โ€” providers is always an empty array โ”€โ”€
    {
      "code":      "WhatsApp",
      "name":      "WhatsApp",
      "price":     1.8500,
      "providers": []
    },
    {
      "code":      "whatsapp-(us)5",
      "name":      "WhatsApp 2",
      "price":     0.7400,
      "providers": []
    },
    {
      "code":      "Signal",
      "name":      "Signal",
      "price":     0.6000,
      "providers": []
    },
    {
      "code":      "Telegram",
      "name":      "Telegram",
      "price":     1.4000,
      "providers": []
    },

    // โ”€โ”€ Multi-pool services โ€” providers array is non-empty, sorted cheapest-first โ”€โ”€
    {
      "code":      "sb_wa",
      "name":      "WhatsApp 3",
      "price":     1.0830,
      "providers": [
        { "provider_id": "3109", "price": 1.0830, "count": 4000 },
        { "provider_id": "2266", "price": 1.0920, "count": 4296 },
        { "provider_id": "3170", "price": 1.3810, "count": 1000 },
        { "provider_id": "2617", "price": 3.6000, "count":  344 }
      ]
    },
    {
      "code":      "sb_tg",
      "name":      "Telegram 2",
      "price":     0.4500,
      "providers": [
        { "provider_id": "2266", "price": 0.4500, "count": 1820 },
        { "provider_id": "3109", "price": 0.6000, "count":  980 }
      ]
    },
    {
      "code":      "sb_fb",
      "name":      "Facebook",
      "price":     0.5000,
      "providers": [
        { "provider_id": "3109", "price": 0.5000, "count": 2100 },
        { "provider_id": "2266", "price": 0.6500, "count":  890 }
      ]
    }
  ]
}
POST

Purchase

https://daisysim.com/api/v1/server7/purchase

Purchase a US virtual number for a specific service. Your wallet is debited by the actual price of the assigned number. Returns the phone number and activation ID needed to poll for your SMS code. Rate limited to 10 requests per 60 seconds.

Pricing is always server-side. Never pass a price in the request body โ€” the server looks up the live price based on the app and optional provider_id you supply. If the service is no longer available, the purchase is rejected with PRICE_VERIFICATION_FAILED โ€” re-fetch /apps and retry.
provider_id is optional for sb_* services. When supplied, that exact pool is used โ€” if it has no stock the purchase is rejected immediately with OUT_OF_STOCK. No fallback to other pools occurs. Omit provider_id to use the cheapest available pool. For non-sb_* services (WhatsApp, WhatsApp 2, Signal, Telegram and its variants) provider_id is ignored entirely.
FieldTypeRequiredDescription
country string required Country ID โ€” e.g. USA
app string required Service code from /apps โ€” e.g. WhatsApp, whatsapp-(us)5, sb_wa, sb_tg, sb_fb
provider_id string optional Pool to use โ€” taken from providers[].provider_id in /apps. Only applicable to sb_* services; ignored for all others. If the pool has no stock the purchase is rejected with OUT_OF_STOCK โ€” no fallback occurs.
app_name string optional Human-readable label stored on the order (defaults to app)
country_name string optional Human-readable country label stored on the order (defaults to country)
cURL โ€” single-provider service
curl -X POST \
  https://daisysim.com/api/v1/server7/purchase \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "country": "USA",
    "app":     "WhatsApp"
  }'
cURL โ€” sb_* service, cheapest pool auto-selected
curl -X POST \
  https://daisysim.com/api/v1/server7/purchase \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "country": "USA",
    "app":     "sb_wa"
  }'
cURL โ€” sb_* service, specific pool pinned
curl -X POST \
  https://daisysim.com/api/v1/server7/purchase \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "country":     "USA",
    "app":         "sb_wa",
    "provider_id": "3109"
  }'
activation_idstringUnique ID โ€” use for /check and /cancel
phone_numberstringThe virtual US number to use for verification
servicestringService name as stored on the order
countrystringCountry label
amount_chargedfloatExact amount deducted from your wallet in USD. Always matches the price of the pool used.
balance_afterfloatWallet balance after this purchase
{
  "success": true,
  "message": "Number purchased successfully.",
  "data": {
    "activation_id":  "32886",
    "phone_number":   "+19401234567",
    "service":        "WhatsApp 3",
    "country":        "USA",
    "amount_charged": 1.0830,
    "balance_after":  12.4000
  }
}
{
  "success": false,
  "error":   "Insufficient wallet balance.",
  "code":    "INSUFFICIENT_BALANCE"
}
{
  "success": false,
  "error":   "No numbers available right now. Please try again shortly.",
  "code":    "OUT_OF_STOCK"
}
GET

Check SMS

https://daisysim.com/api/v1/server7/check/{activation_id}

Poll for an incoming SMS code on a purchased number. The server caches Waiting responses for 15 seconds โ€” polling more frequently returns the cached result with no benefit. Once a code is stored it is returned instantly on every subsequent call, bypassing the cache. Rate limited to 60 requests per 60 seconds.

Recommended polling interval: 15 seconds. This matches the server-side cache TTL for Waiting responses. Polling faster offers no advantage. As soon as a code arrives it bypasses the cache and is returned immediately on the next call.
ParamTypeRequiredDescription
activation_id string required The activation_id returned by /purchase
WaitingNumber active, no SMS received yet โ€” poll again in 15 seconds
CompletedCode received โ€” present in the code field
{
  "success": true,
  "message": "OK",
  "data": {
    "activation_id": "32886",
    "status":        "Waiting",
    "code":          null,
    "phone_number":  "+19401234567"
  }
}
{
  "success": true,
  "message": "OK",
  "data": {
    "activation_id": "32886",
    "status":        "Completed",
    "code":          "847261",
    "phone_number":  "+19401234567"
  }
}
POST

Check All

https://daisysim.com/api/v1/server7/check-all

Batch poll up to 20 activations in a single request. Useful when managing multiple concurrent numbers. The same 15-second server-side cache applies per activation ID.

FieldTypeRequiredDescription
ids array required Array of activation_id strings โ€” max 20
activation_idstringThe queried activation ID
statusstringCompleted, Waiting, Not Found, or Invalid
codestring|nullOTP code โ€” only present when status is Completed, otherwise null
phone_numberstring|nullThe virtual number โ€” present when status is Completed or Waiting. Not included for Not Found or Invalid items.
Drop unresolvable IDs. Items with status Not Found or Invalid will never change โ€” remove them from your polling set immediately. Keep re-submitting only Waiting IDs every 15 seconds.
200 OK
{
  "success": true,
  "message": "OK",
  "data": [
    {
      "activation_id": "32886",
      "status":        "Completed",
      "code":          "847261",
      "phone_number":  "+19401234567"
    },
    {
      "activation_id": "32887",
      "status":        "Waiting",
      "code":          null,
      "phone_number":  "+19402345678"
    },
    {
      // phone_number is not included for Not Found / Invalid items
      "activation_id": "99999",
      "status":        "Not Found",
      "code":          null
    }
  ]
}
POST

Cancel

https://daisysim.com/api/v1/server7/cancel/{activation_id}

Cancel an active number and receive a full refund to your wallet. Only allowed after 3 minutes (180 seconds) from purchase and only if no SMS code has been received. Numbers whose time window has already expired are also fully refunded โ€” no need to check status before calling cancel. Rate limited to 10 requests per 60 seconds.

โณ
3-Minute Lock. Cancellation before 180 seconds from purchase is rejected with TOO_EARLY. The error message includes the exact number of second(s) remaining โ€” e.g. "Cancellation not yet allowed. Please wait 94 more second(s)." โ€” use this value to drive a countdown timer.
Race condition handled. If a code arrives between your last poll and your cancel call, the server returns CODE_RECEIVED (HTTP 422) and includes the code in the data object โ€” treat this exactly like a successful check response.
ParamTypeRequiredDescription
activation_id string required The activation to cancel
activation_idstringThe cancelled activation ID
refundfloatAmount refunded to your wallet in USD
balance_afterfloatWallet balance after the refund
{
  "success": true,
  "message": "Activation cancelled and refunded.",
  "data": {
    "activation_id": "32886",
    "refund":        1.0830,
    "balance_after": 14.2500
  }
}
{
  "success": false,
  "error":   "Cancellation not yet allowed. Please wait 94 more second(s).",
  "code":    "TOO_EARLY"
}
{
  "success": false,
  "error":   "Code already received. Cannot cancel.",
  "code":    "CODE_RECEIVED",
  "data": {
    "activation_id": "32886",
    "phone_number":  "+19401234567",
    "service":       "WhatsApp 3",
    "status":        "Completed",
    "code":          "847261"
  }
}
GET

History

https://daisysim.com/api/v1/server7/history

Returns a paginated list of your activation orders, newest first. Filter by status using the optional query parameter.

ParamTypeRequiredDescription
status string optional Filter: completed (code received), waiting (active, no code yet), or cancelled (expired / cancelled / timed out)
per_page integer optional Results per page โ€” default 20, max 100
activation_idstringUnique activation identifier
phone_numberstringThe virtual US number that was assigned
servicestringService name (e.g. WhatsApp 3)
countrystringCountry label
statusstringCompleted, Waiting, Cancelled, Expired, Timed Out, Rejected, etc. โ€” always treat any order with a non-null code as completed regardless of this string.
codestring|nullReceived OTP code โ€” null if not yet received
amount_chargedfloatAmount deducted from wallet in USD
created_atstringISO 8601 purchase timestamp
completed_atstring|nullISO 8601 timestamp when code was first received. null if code not yet received, or on older completed orders.
200 OK
{
  "success": true,
  "message": "OK",
  "data": {
    "orders": [
      {
        "activation_id":  "32886",
        "phone_number":   "+19401234567",
        "service":        "WhatsApp 3",
        "country":        "USA",
        "status":         "Completed",
        "code":           "847261",
        "amount_charged": 1.0830,
        "created_at":     "2025-03-31T02:20:00+00:00",
        "completed_at":   "2025-03-31T02:21:44+00:00"
      },
      {
        "activation_id":  "32880",
        "phone_number":   "+19403456789",
        "service":        "Signal",
        "country":        "USA",
        "status":         "Cancelled",
        "code":           null,
        "amount_charged": 0.6000,
        "created_at":     "2025-03-31T01:10:00+00:00",
        "completed_at":   null
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page":     20,
      "total":        335,
      "last_page":    17
    }
  }
}

Complete Integration Example

A full purchase-to-code flow in JavaScript โ€” pool selection, 15-second polling via check-all, and cancellation with race-condition handling.

JavaScript (fetch)
const API   = "https://daisysim.com/api/v1/server7";
const KEY   = "YOUR_API_KEY";
const heads = {
  "Authorization": `Bearer ${KEY}`,
  "Content-Type":  "application/json"
};

// 1. Fetch available services (per-account cache: 5 min)
const appsRes = await fetch(`${API}/apps/USA`, { headers: heads });
const apps    = (await appsRes.json()).data;

// 2. Pick a service โ€” sb_wa has multiple pools
const svc = apps.find(a => a.code === "sb_wa");

// 3a. Option A โ€” auto-select cheapest pool (omit provider_id)
const bodyAuto = { country: "USA", app: svc.code };

// 3b. Option B โ€” pin to a specific pool; purchase fails if that pool has no stock
const cheapest   = svc.providers[0];  // already sorted cheapest-first
const bodyPinned = {
  country:     "USA",
  app:         svc.code,
  provider_id: cheapest.provider_id
};

// 4. Purchase (rate limit: 10/min)
const buy = await fetch(`${API}/purchase`, {
  method: "POST", headers: heads, body: JSON.stringify(bodyPinned)
}).then(r => r.json());

if (!buy.success) {
  if (buy.code === "OUT_OF_STOCK") {
    // The pinned pool had no stock โ€” re-fetch /apps and pick a different provider_id
    // Or omit provider_id entirely to use the cheapest available pool
  }
  if (buy.code === "RATE_LIMITED") {
    // buy.error includes wait time โ€” parse and schedule retry
  }
  throw new Error(buy.error);
}

// amount_charged always matches the price of the pool used
const { activation_id, phone_number, amount_charged } = buy.data;
console.log(`Number: ${phone_number}  |  Charged: $${amount_charged}`);

// 5. Poll every 15 seconds (matches server cache TTL โ€” faster polling wastes requests)
let code = null;
const POLL_INTERVAL = 15000;   // 15 s
const MAX_POLLS     = 40;       // ~10 minutes max

for (let i = 0; i < MAX_POLLS; i++) {
  await new Promise(r => setTimeout(r, POLL_INTERVAL));

  const poll = await fetch(`${API}/check-all`, {
    method: "POST", headers: heads,
    body:   JSON.stringify({ ids: [activation_id] })
  }).then(r => r.json());

  const item = poll.data.find(r => r.activation_id === activation_id);

  // Drop unresolvable IDs immediately โ€” they will never change
  if (["Not Found", "Invalid"].includes(item?.status)) break;

  if (item?.status === "Completed") { code = item.code; break; }
}

// 6. Cancel & refund if no code (only allowed after 3-min lock)
if (!code) {
  const cancel = await fetch(`${API}/cancel/${activation_id}`, {
    method: "POST", headers: heads
  }).then(r => r.json());

  if (cancel.success) {
    console.log(`Refunded $${cancel.data.refund}`);

  } else if (cancel.code === "TOO_EARLY") {
    // e.g. "Please wait 94 more second(s)." โ€” parse and retry after that delay
    console.log(cancel.error);

  } else if (cancel.code === "CODE_RECEIVED") {
    // Code arrived in the race window โ€” data.code has it
    code = cancel.data.code;
  }
}

if (code) console.log(`OTP: ${code}`);