zapi.
APISPRICINGDOCSMCP
GitHub

One MCP server,
every AI client.

The Zapi MCP server exposes the whole scraper catalog to your agent through 8 tools — search, schemas, runs, bulk jobs, account, and usage. Copy the config for your client, paste your API key, done.

What it isAuthenticationPer-client setupSDK clientWorkflowsearch_scrapersget_scraper_schemalist_categoriesrun_scraperget_accountget_usagebulk_submitbulk_statusRate limitsPlan gatingError codesTroubleshooting

▸ Overview

  • What it is
  • Authentication

▸ Setup

  • Per-client setup
  • SDK client

▸ Tools

  • Workflow
  • search_scrapers
  • get_scraper_schema
  • list_categories
  • run_scraper
  • get_account
  • get_usage
  • bulk_submit
  • bulk_status

▸ Limits & errors

  • Rate limits
  • Plan gating
  • Error codes

▸ Help

  • Troubleshooting

What it is

Zapi runs a remote MCP server named zapi (version 1.2.0) at https://mcp.zpi.web.id/mcp. Transport is Streamable HTTP, stateless — POST carries JSON-RPC, GET opens SSE, DELETE tears down. There are no resources and no prompts: exactly 8 tools, documented in full below.

MCP (Model Context Protocol) is how AI clients — Claude, Cursor, VS Code Copilot, Gemini, Windsurf, and others — call external tools. Once connected, your agent can discover a scraper and run it without you writing any HTTP code.

Authentication

Authenticate with your Zapi API key (zpi_...) via any of three headers: Authorization: Bearer, x-api-key, or api-key. Missing or invalid keys get a 401. Create a key at Dashboard → API Keys — free, no credit card required.

endpoint + auth
# Zapi MCP endpoint (Streamable HTTP)
https://mcp.zpi.web.id/mcp

# Auth via header — any of these three:
Authorization: Bearer zpi_xxx
x-api-key: zpi_xxx
api-key: zpi_xxx

▸ There is no OAuth server — clients whose connector UI is OAuth-only (Claude Desktop, claude.ai web) can't send a header directly and need the mcp-remote bridge shown in the setup section. Your key is a secret: keep it in your client's config store, never in a repo, and prefer a dedicated revocable key for MCP.

Per-client setup

Find your client, copy its config, and replace zpi_xxx with your key. The endpoint is already baked into every snippet. Clients differ in the config key they expect for a remote HTTP server — the wrong key fails silently:

Config keyClients
urlCursor, Kiro, Cline, Zed (context_servers), JetBrains
httpUrlGemini CLI
serverUrlAntigravity, Windsurf
servers + type: httpVS Code / Copilot
claude mcp addkey: --transport http
claude mcp add --transport http zapi https://mcp.zpi.web.id/mcp \
  --header "Authorization: Bearer zpi_xxx"
.cursor/mcp.jsonkey: url
{
  "mcpServers": {
    "zapi": {
      "url": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
~/.gemini/settings.jsonkey: httpUrl
{
  "mcpServers": {
    "zapi": {
      "httpUrl": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
mcp_config.jsonkey: serverUrl
{
  "mcpServers": {
    "zapi": {
      "serverUrl": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
.kiro/settings/mcp.jsonkey: url
{
  "mcpServers": {
    "zapi": {
      "url": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
.vscode/mcp.jsonkey: servers + type: http
{
  "servers": {
    "zapi": {
      "type": "http",
      "url": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
~/.codeium/windsurf/mcp_config.jsonkey: serverUrl
{
  "mcpServers": {
    "zapi": {
      "serverUrl": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
MCP settings JSONkey: url
{
  "mcpServers": {
    "zapi": {
      "url": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
config.yaml / .continue (verify: docs.continue.dev)key: mcpServers[] + type: streamable-http
{
  "mcpServers": [
    {
      "name": "zapi",
      "type": "streamable-http",
      "url": "https://mcp.zpi.web.id/mcp",
      "requestOptions": {
        "headers": {
          "Authorization": "Bearer zpi_xxx"
        }
      }
    }
  ]
}
settings.jsonkey: context_servers
{
  "context_servers": {
    "zapi": {
      "url": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
mcp.jsonkey: url
{
  "mcpServers": {
    "zapi": {
      "url": "https://mcp.zpi.web.id/mcp",
      "headers": {
        "Authorization": "Bearer zpi_xxx"
      }
    }
  }
}
needs mcp-remote bridge

Claude's connector UI is OAuth-only — there's nowhere to enter a header. Use mcp-remote, a stdio bridge that forwards requests with your auth header:

claude_desktop_config.json
{
  "mcpServers": {
    "zapi": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.zpi.web.id/mcp",
        "--header",
        "Authorization: Bearer zpi_xxx"
      ]
    }
  }
}

In args, --header and its value are two separate array elements — don't combine them into one string.

SDK client

From TypeScript, skip the client config entirely — zpi-sdk/mcp is a zero-dependency MCP client for the same server: JSON-RPC over Streamable HTTP, protocol version 2025-06-18, session tracked via the mcp-session-id header. The initialize handshake runs lazily on first call.

mcp.ts
import { createMcpClient } from "zpi-sdk/mcp";

const mcp = createMcpClient({
  apiKey: "zpi_...",
  baseURL: "https://api.zpi.web.id",
});
const tools = await mcp.listTools();
const result = await mcp.callTool("run_scraper", {
  scraper: "instagram-scraper",
  endpoint: "profile",
  params: { username: "zaadevofc" },
});

listTools() returns the 8 tool definitions; callTool(name, args) invokes one. RPC failures throw ZpiMcpError (with code and data). See the SDK docs for the full client reference.

Tools reference

All 8 tools, in registration order. Read-only tools are free; run_scraper bills per call and bulk_submit bills per item. The recommended workflow:

workflow
# The recommended 3-step workflow for your agent:
search_scrapers query="instagram profile"      # 1) find an endpoint
get_scraper_schema scraper="..." endpoint="..." # 2) only if flagged needsSchemaCall
run_scraper scraper="..." endpoint="profile" params={ "username": "zaadevofc" }

▸ search_scrapers results carry each endpoint's params inline, so your agent can usually go straight to run_scraper — call get_scraper_schema only when a result is flagged needsSchemaCall.

search_scrapers

read-only · free

Natural-language fuzzy search over the catalog. Zero results return did-you-mean suggestions. Each row includes the endpoint's params inline plus minPlan and bulkEnabled. Categories: ai, bypass-tools, downloader, food, games, search-tools, social-media, web-tools.

ParamTypeRequiredPurpose
querystringnoFree-text search, e.g. "instagram profile".
categorystringnoFilter to one category.
cursorstringnoPagination cursor.

get_scraper_schema

read-only · free

Full parameter schema for one endpoint: fields with name / type / required / example, plus minPlan and bulkEnabled. Unknown scraper or endpoint returns an error steering back to search_scrapers.

ParamTypeRequiredPurpose
scraperstringyesScraper slug.
endpointstringyesEndpoint slug.

list_categories

read-only · free

No input. Returns every catalog category with its scraper count — useful for scoping before a search.

run_scraper

bills per call

Executes one scraper endpoint. Marked readOnlyHint: false — clients should not auto-approve it. Path params in params fill the URL template automatically and the HTTP method is chosen for you.

ParamTypeRequiredPurpose
scraperstringyesScraper slug.
endpointstringyesEndpoint slug.
paramsobjectnoEndpoint params; path params (username, slug, …) go here too.

Success returns { data, project, cache, meta } where meta is { billed, costUnits, remaining, resetAt }. Failures set isError with a code from the error table — including plan_upgrade_required when the endpoint's minPlan is above your tier.

get_account

read-only · free

No input. Returns your tier, planStatus (free | active | expired), planExpiresAt, and the API key label ({ name, keyPrefix, enabled }). The key secret is never returned. Quota numbers live in get_usage, not here.

get_usage

read-only · free

No input. Returns a summary ({ totalRequests, requests24h, errors24h, successRate24h }) plus your quota ({ tier, used, limit, remaining, resetAt }— resetAt is the 1st of next month, UTC). Check it before run_scraper to avoid burning quota blind.

bulk_submit

bills per itempro plan or higher

Submits a batch job. Requires a Pro+ plan and bulkEnabled on the target endpoint, with per-tier item caps. Returns immediately with { jobId, status: "queued", total } — it never waits; poll with bulk_status.

ParamTypeRequiredPurpose
scraperstringyesScraper slug.
endpointstringyesEndpoint slug.
urlsarrayyesURL strings or per-item param objects; duplicates are removed.
idempotencyKeystringnoSame key returns the same job — no double submission.

bulk_status

read-only · free

Polls one job. Returns { jobId, status, total, succeeded, failed, scraperSlug, endpointSlug, items } where each item is { url, status, error? }. Unknown or other-account jobs return job_not_found — job ids can't be enumerated.

ParamTypeRequiredPurpose
jobIdstringyesId from bulk_submit.

Rate limits

MCP shares rate limits with the REST API. The per-minute window counts all requests; the per-month window counts only billable ones. Free tier: 100 requests/minute, 2,000 billable requests/month — paid tiers on Pricing. A 429 response is JSON with window: "minute" | "month" telling you which limit you hit.

HeaderMeaning
X-RateLimit-LimitYour plan's limit for the window.
X-RateLimit-Remaining-MinuteRequests left in the current minute.
X-RateLimit-Remaining-MonthBillable requests left this month.
X-Plan-ExpiredPresent when a paid plan has lapsed — free limits apply.

Plan gating

  • Every endpoint declares a minPlan — calls below it fail with plan_upgrade_required (including requiredPlan and upgradeUrl).
  • Bulk needs three things at once: a Pro+ plan, bulkEnabled on the endpoint, and staying under your tier's per-job item cap.
  • An expired paid plan silently drops you to free limits — check get_account (planStatus) if calls start rate-limiting unexpectedly.

Error codes

Tool failures set isError with a machine code in structuredContent.code:

CodeMeaning
unauthenticatedMissing or invalid API key (401).
plan_upgrade_requiredEndpoint needs a higher tier; includes requiredPlan + upgradeUrl.
invalid_paramsInput failed validation; includes per-field details.
not_foundUnknown scraper or endpoint.
job_not_foundbulk_status jobId unknown or owned by another account.
disabledEndpoint is temporarily disabled.
method_not_allowedEndpoint doesn't support the resolved HTTP method.
stream_unsupportedStreaming endpoint — not runnable through run_scraper.
module_not_registeredScraper module not loaded on the worker.
scraper_errorThe scraper ran and failed upstream.
client_errorOther 4xx from the API.
internal_errorUnexpected server failure — retry later.

Troubleshooting

  • 401 / unauthenticated — wrong, revoked, or missing key. Check the header name (any of Authorization: Bearer, x-api-key, api-key) and rotate at /dashboard/keys.
  • Connected but unauthorized — your client is OAuth-only and never sent the header. Use the mcp-remote bridge.
  • 403 from a browser — the origin guard blocks browser origins that aren't allow-listed. Call the server from a client or backend instead of in-page fetch.
  • Client doesn't see the server — usually the wrong config key. Check the config-key table (url / httpUrl / serverUrl / servers).
  • 429 — check which window you hit (window: "minute" | "month") and the X-RateLimit headers; a minute limit clears itself, a month limit means upgrading.
  • plan_upgrade_required — the endpoint's minPlan is above your tier. See Pricing.
Get your API key →Read the SDK docs →
Major outage
zapi. — Public REST API catalog. No fabricated numbers.
TermsPrivacyAUP© 2026