Spoofy for developers and agents

Everything the app does, from code.

Followers, views, likes, members, token-page reactions and site visits for any link on ~35 platforms. Same catalog, same prices, same balance as the web app — reachable three ways.

Quickstart

Three calls to an order

Browse and price without a key. To order, create a key in your dashboard and put it in SPOOFY_API_KEY. Every amount is US dollars as a decimal string ("4.99").

bash
# 1. Find a service (no key needed) — here: X followers
curl "https://spoofy.social/api/v1/services?platform=x"

# 2. Get the exact price for 1,000 (no key needed)
curl -X POST https://spoofy.social/api/v1/quotes \
  -H "Content-Type: application/json" \
  -d '{"service": 12, "quantity": 1000}'

# 3. Place the order — spends balance. Add "dry_run": true to check without charging.
curl -X POST https://spoofy.social/api/v1/orders \
  -H "Authorization: Bearer $SPOOFY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"service": 12, "link": "https://x.com/yourname", "quantity": 1000}'

Have a link rather than a platform? GET /api/v1/links?url=… tells you what it points to and returns only the services that fit it.

Authentication

Keys and safety rails

Send Authorization: Bearer fimi_live_…. Create and revoke keys at your dashboard; the full key is shown once and we keep only its hash. Keys can only be made from the signed-in dashboard — a key can never mint keys or read webhook secrets. Up to 20 active keys per account.

Read-only keys

scopes: ["read"] — can browse, quote and read orders, never spend.

Spend caps

dailyCap (per UTC day, net of refunds) and perOrderCap, in USD.

Platform allow-list

platforms: ["x", "tiktok"] — orders for anything else are refused.

Dry runs

"dry_run": true validates and prices an order, shows your balance, charges nothing.

Idempotency

Idempotency-Key header on every order: a retry returns the first order, never a second charge.

Labelled orders

Every order made with a key carries it, so the dashboard shows which key (or agent) spent what.

No key can withdraw money. The browser session of the web app works on the same endpoints, so the app is just another client of this API.

Base URL https://spoofy.social/api/v1

API reference

Generated from the OpenAPI 3.1 document — the same file code generators and agents read.

Catalog

What can be bought, for which link, at what price. No auth needed.

get/api/v1/servicesNo auth

List services

The full public catalog: every service with its price per 1,000, range and package suggestions.

platformqueryOnly one platform, e.g. `x`, `tiktok`, `telegram`.
request
curl "https://spoofy.social/api/v1/services?platform=x"
response 200
{
  "services": [
    {
      "id": 12,
      "platform": "x",
      "platformLabel": "X",
      "target": "profile",
      "metric": "followers",
      "metricLabel": "Followers",
      "tier": "standard",
      "name": "X Followers — Standard",
      "description": "Real-looking accounts, steady delivery, 30-day refill.",
      "linkHint": "https://x.com/yourname",
      "minQty": 100,
      "maxQty": 50000,
      "pricePer1k": "4.99",
      "pricePer1kMicros": "4990000",
      "refillDays": 30,
      "refillable": true,
      "cancelable": true,
      "startTime": "0–2 h",
      "speed": "2K / day",
      "tags": [
        "refill"
      ],
      "startEstimate": "Usually starts within 1 h",
      "delivery": {
        "startMinutes": 60,
        "perDay": 2000,
        "avgMinutesPer1k": null
      },
      "packages": [
        {
          "quantity": 100,
          "price": "0.50",
          "eta": "~3 h",
          "etaMinutes": 132
        },
        {
          "quantity": 500,
          "price": "2.50",
          "eta": "~7 h",
          "etaMinutes": 420
        },
        {
          "quantity": 1000,
          "price": "4.99",
          "eta": "~13 h",
          "etaMinutes": 780
        },
        {
          "quantity": 5000,
          "price": "24.95",
          "eta": "~3 days",
          "etaMinutes": 3660
        },
        {
          "quantity": 50000,
          "price": "249.50",
          "eta": "~25 days",
          "etaMinutes": 36060
        }
      ]
    }
  ]
}

Errors: 500 internal

get/api/v1/services/{id}No auth

Get a service

One service by id.

id*pathService id.
request
curl "https://spoofy.social/api/v1/services/12"
response 200
{
  "service": {
    "id": 12,
    "platform": "x",
    "platformLabel": "X",
    "target": "profile",
    "metric": "followers",
    "metricLabel": "Followers",
    "tier": "standard",
    "name": "X Followers — Standard",
    "description": "Real-looking accounts, steady delivery, 30-day refill.",
    "linkHint": "https://x.com/yourname",
    "minQty": 100,
    "maxQty": 50000,
    "pricePer1k": "4.99",
    "pricePer1kMicros": "4990000",
    "refillDays": 30,
    "refillable": true,
    "cancelable": true,
    "startTime": "0–2 h",
    "speed": "2K / day",
    "tags": [
      "refill"
    ],
    "startEstimate": "Usually starts within 1 h",
    "delivery": {
      "startMinutes": 60,
      "perDay": 2000,
      "avgMinutesPer1k": null
    },
    "packages": [
      {
        "quantity": 100,
        "price": "0.50",
        "eta": "~3 h",
        "etaMinutes": 132
      },
      {
        "quantity": 500,
        "price": "2.50",
        "eta": "~7 h",
        "etaMinutes": 420
      },
      {
        "quantity": 1000,
        "price": "4.99",
        "eta": "~13 h",
        "etaMinutes": 780
      },
      {
        "quantity": 5000,
        "price": "24.95",
        "eta": "~3 days",
        "etaMinutes": 3660
      },
      {
        "quantity": 50000,
        "price": "249.50",
        "eta": "~25 days",
        "etaMinutes": 36060
      }
    ]
  }
}

Errors: 404 not_found

post/api/v1/quotesNo auth

Get a quote

The exact price of a quantity. Nothing is charged and no auth is needed.

request
curl -X POST "https://spoofy.social/api/v1/quotes" \
  -H "Content-Type: application/json" \
  -d '{"service":12,"quantity":1000}'
response 200
{
  "quote": {
    "service": 12,
    "quantity": 1000,
    "pricePer1k": "4.99",
    "total": "4.99",
    "currency": "USD"
  }
}

Errors: 400 bad_quantity · 404 service_unavailable

Orders

Place, read, refill and cancel orders. Spends balance.

get/api/v1/ordersAPI key

List orders

Your orders, newest first. Page with before = the smallest order number you have.

statusqueryFilter by status.
limitquery1–200, default 50.
beforequeryOnly orders with a smaller number.
request
curl "https://spoofy.social/api/v1/orders" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 200
{
  "orders": [
    {
      "id": 1042,
      "service": {
        "id": 12,
        "name": "X Followers — Standard",
        "platform": "x",
        "metric": "followers"
      },
      "link": "https://x.com/yourname",
      "quantity": 1000,
      "charge": "4.99",
      "refunded": "0.00",
      "status": "processing",
      "startCount": 1520,
      "delivered": 300,
      "remains": 700,
      "refillable": false,
      "cancelable": true,
      "source": "api",
      "createdAt": "2026-10-16T12:00:00.000Z",
      "finishedAt": null,
      "expectedAt": "2026-10-17T01:00:00.000Z"
    }
  ]
}

Errors: 401 invalid_api_key

post/api/v1/ordersAPI key

Place an order

Charges your balance and starts delivery. Send an Idempotency-Key header so a retry never double-charges. With dry_run: true nothing is charged: you get the price, your balance and whether it is enough.

Idempotency-KeyheaderAny unique string (≤ 120 chars) per intended order. Retrying with the same key returns the first order instead of charging twice.
request
curl -X POST "https://spoofy.social/api/v1/orders" \
  -H "Authorization: Bearer $SPOOFY_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"service":12,"link":"https://x.com/yourname","quantity":1000}'

201 — Placed and charged.

response 201
{
  "order": {
    "id": 1042,
    "service": {
      "id": 12,
      "name": "X Followers — Standard",
      "platform": "x",
      "metric": "followers"
    },
    "link": "https://x.com/yourname",
    "quantity": 1000,
    "charge": "4.99",
    "refunded": "0.00",
    "status": "pending",
    "startCount": null,
    "delivered": null,
    "remains": null,
    "refillable": false,
    "cancelable": true,
    "source": "api",
    "createdAt": "2026-10-16T12:00:00.000Z",
    "finishedAt": null,
    "expectedAt": "2026-10-17T01:00:00.000Z"
  },
  "replayed": false,
  "charged": "4.99"
}

200 — Dry run answer — or a replay of an earlier order with the same Idempotency-Key (replayed: true).

response 200
{
  "dry_run": true,
  "quote": {
    "service": 12,
    "quantity": 1000,
    "total": "4.99",
    "currency": "USD"
  },
  "balance": "12.00",
  "sufficient": true
}

Errors: 400 invalid_body · 401 invalid_api_key · 402 insufficient_balance · 403 over_order_cap

get/api/v1/orders/{id}API key

Get an order

Status and progress of one order.

id*pathOrder number.
request
curl "https://spoofy.social/api/v1/orders/1042" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 200
{
  "order": {
    "id": 1042,
    "service": {
      "id": 12,
      "name": "X Followers — Standard",
      "platform": "x",
      "metric": "followers"
    },
    "link": "https://x.com/yourname",
    "quantity": 1000,
    "charge": "4.99",
    "refunded": "0.00",
    "status": "processing",
    "startCount": 1520,
    "delivered": 300,
    "remains": 700,
    "refillable": false,
    "cancelable": true,
    "source": "api",
    "createdAt": "2026-10-16T12:00:00.000Z",
    "finishedAt": null,
    "expectedAt": "2026-10-17T01:00:00.000Z"
  }
}

Errors: 404 not_found

post/api/v1/orders/{id}/refillAPI key

Request a refill

For a completed order of a refillable service whose count dropped. Carried out in the background.

id*pathOrder number.
request
curl -X POST "https://spoofy.social/api/v1/orders/1042/refill" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 202
{
  "ok": true,
  "order": 1042,
  "action": "refill",
  "status": "requested"
}

Errors: 400 not_refillable · 404 not_found

post/api/v1/orders/{id}/cancelAPI key

Request a cancel

For a pending or processing order of a cancelable service. Whatever was not delivered is refunded to your balance.

id*pathOrder number.
request
curl -X POST "https://spoofy.social/api/v1/orders/1042/cancel" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 202
{
  "ok": true,
  "order": 1042,
  "action": "cancel",
  "status": "requested"
}

Errors: 400 not_cancelable · 404 not_found

post/api/v1/uploadsAPI key

Upload an image

An image for a done-for-you order (logo, banner): multipart field file, or the raw bytes with an image Content-Type. PNG, JPG, WEBP or GIF up to 4 MB. Send the returned id in the order’s details; an upload no order uses is deleted after a day.

request
curl -X POST "https://spoofy.social/api/v1/uploads" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 201
{
  "upload": {
    "id": "cm2uploadlogo0000000000000",
    "mime": "image/png",
    "size": 48213,
    "width": 512,
    "height": 512
  }
}

Errors: 400 invalid_body · 401 invalid_api_key · 403 over_order_cap

Account

Balance.

get/api/v1/balanceAPI key

Get balance

Your spendable balance in USD.

request
curl "https://spoofy.social/api/v1/balance" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 200
{
  "balance": "12.00",
  "currency": "USD"
}

Errors: 401 invalid_api_key

Deposits

Top up the balance with USDG (Robinhood Chain) or the project token (10 % off).

get/api/v1/deposits/methodsAPI key

Deposit methods

How to top up: your own deposit address per asset (USDG on Robinhood Chain; the project token at 10 % off once live).

request
curl "https://spoofy.social/api/v1/deposits/methods" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 200
{
  "methods": [
    {
      "id": "usdg",
      "asset": "USDG",
      "chain": {
        "id": 4663,
        "name": "Robinhood Chain",
        "explorer": "https://rh-scan.com"
      },
      "tokenAddress": "0x0000000000000000000000000000000000000001",
      "decimals": 6,
      "address": "0x1111111111111111111111111111111111111111",
      "treasury": "0x2222222222222222222222222222222222222222",
      "minimum": "1.00",
      "discountPct": 0,
      "priceUsd": null,
      "note": "Send any amount of USDG on Robinhood Chain to your address; it is credited 1 USDG = $1."
    }
  ]
}

Errors: 401 invalid_api_key

get/api/v1/depositsAPI key

List deposits

Your deposits, newest first.

request
curl "https://spoofy.social/api/v1/deposits" \
  -H "Authorization: Bearer $SPOOFY_API_KEY"
response 200
{
  "deposits": [
    {
      "id": "cmkd3p0s1t0000qrs",
      "asset": "USDG",
      "amount": "25.00",
      "value": "25.00",
      "credit": "25.00",
      "txHash": "0xabababababababababababababababababababababababababababababababab",
      "status": "credited",
      "createdAt": "2026-10-16T11:58:00.000Z",
      "creditedAt": "2026-10-16T11:58:20.000Z"
    }
  ]
}

Errors: 401 invalid_api_key

post/api/v1/deposits/claimAPI key

Claim a wallet payment

After paying the treasury from a wallet, send the transaction hash: it is verified on chain and credited.

request
curl -X POST "https://spoofy.social/api/v1/deposits/claim" \
  -H "Authorization: Bearer $SPOOFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"txHash":"0xabababababababababababababababababababababababababababababababab"}'
response 200
{
  "deposit": {
    "id": "cmkd3p0s1t0000qrs",
    "asset": "USDG",
    "amount": "25.00",
    "value": "25.00",
    "credit": "25.00",
    "txHash": "0xabababababababababababababababababababababababababababababababab",
    "status": "credited",
    "createdAt": "2026-10-16T11:58:00.000Z",
    "creditedAt": "2026-10-16T11:58:20.000Z"
  }
}

Errors: 400 invalid_body · 401 invalid_api_key

API keys

Create and revoke keys. Signed-in dashboard session only — a key cannot mint keys.

get/api/v1/keysDashboard session only

List API keys

Every key of the signed-in user, revoked ones included. Secrets are never shown again.

Called by the signed-in dashboard; an API key is refused here.

response 200
{
  "keys": [
    {
      "id": "cmk1a2b3c0000xyz",
      "name": "Growth agent",
      "prefix": "fimi_live_Ab3x",
      "scopes": [
        "read",
        "write"
      ],
      "dailyCap": "50.00",
      "perOrderCap": "10.00",
      "platforms": [
        "x",
        "tiktok"
      ],
      "lastUsedAt": null,
      "createdAt": "2026-10-16T12:00:00.000Z",
      "revokedAt": null
    }
  ]
}

Errors: 401 invalid_api_key · 403 session_required

post/api/v1/keysDashboard session only

Create an API key

Every field is optional. scopes: ["read"] makes a read-only key. Caps are USD strings; platforms limits which platforms the key may order for. The full key is in this answer only — store it now. Up to 20 active keys.

request body
{
  "name": "Growth agent",
  "scopes": [
    "read",
    "write"
  ],
  "dailyCap": "50.00",
  "perOrderCap": "10.00",
  "platforms": [
    "x",
    "tiktok"
  ]
}

Called by the signed-in dashboard; an API key is refused here.

response 201
{
  "key": "fimi_live_Ab3x…(48 chars)",
  "apiKey": {
    "id": "cmk1a2b3c0000xyz",
    "name": "Growth agent",
    "prefix": "fimi_live_Ab3x",
    "scopes": [
      "read",
      "write"
    ],
    "dailyCap": "50.00",
    "perOrderCap": "10.00",
    "platforms": [
      "x",
      "tiktok"
    ],
    "lastUsedAt": null,
    "createdAt": "2026-10-16T12:00:00.000Z",
    "revokedAt": null
  }
}

Errors: 400 invalid_body · 401 invalid_api_key · 409 too_many_keys

delete/api/v1/keys/{id}Dashboard session only

Revoke an API key

The key stops working at once.

id*pathKey id.

Called by the signed-in dashboard; an API key is refused here.

response 200
{
  "ok": true
}

Errors: 404 not_found

Webhooks

Signed event delivery to your endpoint. Session only.

get/api/v1/webhooksDashboard session only

List webhooks

Your active webhook endpoints.

Called by the signed-in dashboard; an API key is refused here.

response 200
{
  "webhooks": [
    {
      "id": "cmk9w8v7u0000abc",
      "url": "https://example.com/spoofy-webhook",
      "events": [
        "order.updated"
      ],
      "active": true,
      "createdAt": "2026-10-16T12:00:00.000Z"
    }
  ]
}

Errors: 401 invalid_api_key

post/api/v1/webhooksDashboard session only

Add a webhook

Public https URL; no events means every event. The signing secret is in this answer only. Up to 10 webhooks.

request body
{
  "url": "https://example.com/spoofy-webhook",
  "events": [
    "order.updated"
  ]
}

Called by the signed-in dashboard; an API key is refused here.

response 201
{
  "webhook": {
    "id": "cmk9w8v7u0000abc",
    "url": "https://example.com/spoofy-webhook",
    "events": [
      "order.updated"
    ],
    "active": true,
    "createdAt": "2026-10-16T12:00:00.000Z",
    "secret": "whsec_…(shown once)"
  }
}

Errors: 400 bad_webhook_url · 409 too_many_keys

delete/api/v1/webhooks/{id}Dashboard session only

Remove a webhook

Stops delivery to this endpoint; queued deliveries are dropped.

id*pathWebhook id.

Called by the signed-in dashboard; an API key is refused here.

response 200
{
  "ok": true
}

Errors: 404 not_found

Meta

This document.

get/api/v1/openapi.jsonNo auth

This document

The OpenAPI 3.1 description of the API, for code generators and agents.

request
curl "https://spoofy.social/api/v1/openapi.json"

Every failure, one shape

Errors

error
{
  "error": {
    "code": "insufficient_balance",
    "message": "Your balance is $1.00 and this order costs $4.99. Top up $3.99 to place it.",
    "details": {
      "balance": "1.00",
      "required": "4.99",
      "shortfall": "3.99"
    }
  }
}

Branch on code; show message to people. Internal details never appear in an answer.

HTTPcodemeaning
400invalid_bodyThe JSON body is missing or has the wrong shape. The message says what to send.
400bad_linkThe link is not a URL we can read. Send the full address, e.g. https://x.com/yourname.
400wrong_platformThe service is for another platform than the link (e.g. a TikTok service with an X link).
400bad_quantityQuantity outside the service range. `details.min` / `details.max` hold the range.
400not_refillableThis order cannot be refilled (service has no refill, or the order is not completed).
400not_cancelableThis order cannot be cancelled now.
400chain_not_supportedThe token is on a chain this service does not serve yet (GMGN live viewers: Robinhood Chain only).
400details_requiredA done-for-you service needs a field of its `form`. `details.field` names it.
400bad_detailsA `details` answer is not right (not a link, too long, an unknown or unsuitable upload). `details.field` names it.
400no_fileThe upload carried no file.
400bad_webhook_urlWebhook URL must be public https://.
401unauthorizedNo API key and no session. Send Authorization: Bearer fimi_live_….
401invalid_api_keyThe API key is wrong or revoked.
402insufficient_balanceBalance too low. `details.shortfall` is the USD amount to top up.
403read_only_keyThis key has the read scope only and cannot place, refill or cancel orders.
403platform_not_allowedThe service's platform is not on this key's allow-list.
403over_order_capThe order costs more than this key's per-order cap.
403over_daily_capThe order would take this key over its daily spend cap (UTC day, net of refunds).
403session_requiredKeys and webhooks are managed from the signed-in dashboard; an API key cannot manage them.
403bad_originA browser request from another site was refused.
404not_foundNo such order / key / webhook on your account.
404service_unavailableThe service id does not exist or is paused right now.
409too_many_keysYou already have 20 active keys. Revoke one first.
409too_many_webhooksYou already have 10 webhooks. Remove one first.
409link_busyAs many as this service allows are already running on that link (GMGN: 120 live viewers per token). `details.available` is how many more fit now.
413too_largeThe upload is over 4 MB.
415not_an_imageThe upload is not a PNG, JPG, WEBP or GIF image.
429too_many_uploadsToo many uploads in the last hour.
500internalSomething failed on our side. Safe to retry; writes with the same Idempotency-Key never double-charge.

Signed events

Webhooks

Add an https endpoint in the dashboard and pick events: order.updated, deposit.credited, balance.low (none picked = all). We POST JSON { id, type, created, data } with two headers:

  • Fimi-Signature: t=<unix>,v1=<hex> — HMAC-SHA256 of "<t>.<raw body>" with your whsec_… secret.
  • Fimi-Event-Id — the event id. Delivery is at-least-once: drop ids you have seen.

Answer 2xx within 10 seconds. Anything else is retried with exponential backoff (1 min, 2, 4 … up to 6 h apart) for about 24 hours.

node — verify a delivery
import crypto from 'node:crypto';

// header = the Fimi-Signature request header; rawBody = the body exactly as received (a string, not re-serialised)
export function verifySignature(header, rawBody, secret, toleranceSeconds = 300) {
  if (!header) return false;
  let t = null;
  const sigs = [];
  for (const part of header.split(',')) {
    const [k, v] = part.split('=');
    if (k === 't') t = Number(v);
    if (k === 'v1') sigs.push(v);
  }
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false; // stale: possible replay
  const want = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return sigs.some((s) => s.length === want.length && crypto.timingSafeEqual(Buffer.from(s), Buffer.from(want)));
}

// Express: keep the raw body for this route
app.post('/spoofy-webhook', express.text({ type: '*/*' }), (req, res) => {
  if (!verifySignature(req.get('Fimi-Signature'), req.body, process.env.SPOOFY_WEBHOOK_SECRET)) return res.sendStatus(400);
  const event = JSON.parse(req.body); // { id, type, created, data }
  // drop duplicates by event.id (also in the Fimi-Event-Id header): deliveries are at-least-once
  res.sendStatus(200);
});
order.updated
{
  "id": "evt_cmk5e6f7g0000hij",
  "type": "order.updated",
  "created": 1792152000,
  "data": {
    "order": {
      "id": 1042,
      "service": {
        "id": 12,
        "name": "X Followers — Standard",
        "platform": "x",
        "metric": "followers"
      },
      "link": "https://x.com/yourname",
      "quantity": 1000,
      "charge": "4.99",
      "refunded": "0.00",
      "status": "processing",
      "startCount": 1520,
      "delivered": 300,
      "remains": 700,
      "refillable": false,
      "cancelable": true,
      "source": "api",
      "createdAt": "2026-10-16T12:00:00.000Z",
      "finishedAt": null,
      "expectedAt": "2026-10-17T01:00:00.000Z"
    }
  }
}

https://spoofy.social/api/v2

SMM-compatible endpoint

The key + action format SMM tools already speak. Add Spoofy as a provider with the URL https://spoofy.social/api/v2 and your Spoofy API key — same caps, same prices, same orders as the REST API. Form-encoded, JSON or query string all work (prefer POST: a key in a URL ends up in logs).

actionparamsanswer
services[{service, name, type, category, rate, min, max, refill, cancel}]
addservice, link, quantity{order}
statusorder{charge, start_count, status, remains, currency}
statusorders=1,2,3 (≤100){"1": {…}, "2": {error}}
refillorder | orders{refill} | [{order, refill}]
cancelorder | orders{order, cancel} | [{order, cancel}]
balance{balance, currency: "USD"}

rate is USD per 1,000. Status words: Pending, Processing, In progress, Completed, Partial, Canceled; charge is net of refunds. Errors are {"error": "…"} with HTTP 200, as SMM tools expect.

bash
curl -X POST https://spoofy.social/api/v2 -d key=$SPOOFY_API_KEY -d action=services
curl -X POST https://spoofy.social/api/v2 -d key=$SPOOFY_API_KEY -d action=add -d service=12 -d link=https://x.com/yourname -d quantity=1000
curl -X POST https://spoofy.social/api/v2 -d key=$SPOOFY_API_KEY -d action=status -d order=1042
curl -X POST https://spoofy.social/api/v2 -d key=$SPOOFY_API_KEY -d action=status -d orders=1042,1043
curl -X POST https://spoofy.social/api/v2 -d key=$SPOOFY_API_KEY -d action=balance

https://spoofy.social/mcp

Use Spoofy from your AI agent

A remote MCP server over Streamable HTTP. Your agent finds services from a link or a goal in plain words, quotes, orders, follows delivery and tells you how to top up. Give it a key with a daily cap and a per-order cap — and a read-only key if it should only look.

Claude Code — Run in a terminal:

bash
claude mcp add --transport http spoofy https://spoofy.social/mcp --header "Authorization: Bearer YOUR_KEY"

Cursor — Add to .cursor/mcp.json in your project (or ~/.cursor/mcp.json for every project):

json
{
  "mcpServers": {
    "spoofy": {
      "url": "https://spoofy.social/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_KEY"
      }
    }
  }
}

Claude Desktop — Settings → Developer → Edit Config (claude_desktop_config.json), then restart Claude. Needs Node.js:

json
{
  "mcpServers": {
    "spoofy": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://spoofy.social/mcp",
        "--header",
        "Authorization:${SPOOFY_AUTH}"
      ],
      "env": {
        "SPOOFY_AUTH": "Bearer YOUR_KEY"
      }
    }
  }
}

VS Code (Copilot agent mode) — Add to .vscode/mcp.json:

json
{
  "servers": {
    "spoofy": {
      "type": "http",
      "url": "https://spoofy.social/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_KEY"
      }
    }
  }
}

Any MCP client — Streamable HTTP transport, stateless (no session needed):

bash
URL:     https://spoofy.social/mcp
Header:  Authorization: Bearer YOUR_KEY
Method:  POST JSON-RPC 2.0 (Accept: application/json, text/event-stream)

Tools

find_services

Find what Spoofy can do for a link or a goal, with prices. ALWAYS call this first: service ids come from here. Give link (any profile, post, video, channel, group, token page or website URL) and/or goal in plain words, e.g. "500 followers on my tiktok" or "cheap views for this YouTube video". With a link you only get services that fit that exact link (a profile link gets followers, a post link gets likes/views). Returns matching services: service (the id to quote/order), name, tier (basic/standard/premium), pricePer1k (USD per 1,000 units), min/max quantity, start time, speed, refill days, preset packages with USD prices, and — when the goal names a quantity — a suggestion with the exact total for that quantity. Free; spends nothing.

get_quote

Exact price in USD for a quantity of one service (from find_services). Checks the quantity is within the service range. Free; spends nothing. Use it to tell the user the cost before place_order.

place_order

acts

Place an order. THIS SPENDS THE USER'S REAL BALANCE (USD) and starts delivery to the link; it cannot be undone except by cancel_order where the service allows it. Before calling: get the price with get_quote and get the user's explicit agreement to that price. Then call with confirm: true. Without confirm: true nothing is charged and you get the price back so you can ask. dry_run: true validates everything (link fits the service, quantity range, key limits) and returns price, balance and whether it is enough — never charges. Pass a fresh idempotency_key per intended order so a retry never charges twice. Errors: insufficient_balance (details.shortfall = USD to top up — call get_top_up_info), over_order_cap / over_daily_cap / platform_not_allowed / read_only_key (this key's limits), wrong_platform, bad_quantity, link_busy (too many running on that link: details.available), chain_not_supported. Done-for-you services (kind "manual": listings) need a form with images — those are ordered on the website.

get_order

Status and progress of one order: status (pending, processing, completed, partial, refunded, cancelled, failed), start count, delivered, remains, charge and refunds in USD, and whether refill/cancel are possible now.

list_orders

The user's orders, newest first. Filter by status; page with before = the smallest order number already seen.

request_refill

acts

Ask for a refill of a completed order whose count dropped (only when the order says refillable: true). Free. Carried out in the background; check progress with get_order.

cancel_order

acts

Ask to cancel a pending or processing order (only when the order says cancelable: true). Whatever was not delivered is refunded to the balance in USD. Confirm with the user first.

get_balance

The user's spendable balance in USD. Orders are paid from it.

get_top_up_info

How the user adds money to the balance: their own deposit address for USDG on Robinhood Chain (credited 1 USDG = $1) and, once live, the project token at 10 % off, plus the top-up page link. Use it when an order fails with insufficient_balance. You cannot move money yourself — give the user the address or the link.

place_order never charges unless the agent passes confirm: true after you agreed to the price; without it, the agent gets the price back to ask you. Raw JSON-RPC, for testing:

bash
curl -X POST https://spoofy.social/mcp \
  -H "Authorization: Bearer $SPOOFY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"find_services","arguments":{"goal":"500 followers","link":"https://x.com/yourname"}}}'

@fimi/sdk

TypeScript SDK

Zero dependencies, fetch-based, typed from the same shapes as the OpenAPI document. Runs in Node 18+, Bun, Deno, browsers and edge runtimes. Every orders.create sends an auto-generated idempotency key.

typescript
import { Spoofy } from '@fimi/sdk';

const spoofy = new Spoofy({ apiKey: process.env.SPOOFY_API_KEY });

const { link, services } = await spoofy.links.inspect('https://x.com/yourname');
const service = services.find((s) => s.metric === 'followers')!;
const quote = await spoofy.quotes.create({ service: service.id, quantity: 1000 });

const check = await spoofy.orders.create({ service: service.id, link: link.url, quantity: 1000, dry_run: true });
if (check.sufficient) {
  const { order } = await spoofy.orders.create({ service: service.id, link: link.url, quantity: 1000 });
  console.log(`#${order.id} ${order.status} — charged $${order.charge} (quoted $${quote.total})`);
}

Webhooks: await spoofy.webhooks.verify(signatureHeader, rawBody, secret). Errors throw SpoofyError with status, code and message.

Machine-readable

For LLMs and crawlers