Skip to main content

REST API

Identity Verification APIs

APIs that fetch an institution's data in the name of one person. Most run asynchronously: you submit, the person approves on their phone, and you collect the result by polling.

Overview

A request to these APIs can answer 202 and finish later. Some certificate paths finish immediately with 200, so branch on the response code instead of assuming either.

textFlow
POST /api/v1/proxy/{slug}
  -> 202 accepted  (job_id, poll_url)
GET  /api/v1/jobs/{job_id}   repeat every retry_after_ms
  -> 200 running | 200 done | 422 failed | 410 expired | 504 timeout

Inputs

Supported auth values differ per API: check the auth enum on the Endpoints tab of the catalog page. Missing inputs fail before acceptance with 400 INVALID_PARAMETER and are not billed. Resident numbers and certificates are encrypted at rest and masked in logs.

Field
Meaning
auth"quick" (phone approval) or "pki" (certificate). Each API lists the values it accepts.
ownerNameQuick. Required. Full name of the person.
phoneNoQuick. Required. Mobile number, digits only (01012345678).
birthdayQuick. YYYYMMDD. One of birthday or identity is required.
identity13-digit resident number, digits only. Replaces birthday. Some APIs require it.
authProviderQuick. Only when the API accepts it. Default kakao.
certPKI. certContent and keyContent are the base64 of the files; plus certPassword.
jsonQuick body
{
  "auth": "quick",
  "ownerName": "홍길동",
  "phoneNo": "01012345678",
  "birthday": "19900101"
}
jsonPKI body
{
  "auth": "pki",
  "cert": {
    "certContent": "<base64 of the certificate file>",
    "keyContent": "<base64 of the private key file>",
    "certPassword": "<certificate password>"
  }
}

Submit

Send an X-Idempotency-Key. Resubmitting with the same key replays the same job_id and is not charged twice. poll_url is relative to /api, so the full URL is https://apitree.ai/api/v1/jobs/{job_id}.

bashSubmit
curl -X POST https://apitree.ai/api/v1/proxy/{slug} \
  -H "Authorization: Bearer nxs_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: <unique-id>" \
  -d '{"auth":"quick","ownerName":"홍길동","phoneNo":"01012345678","birthday":"19900101"}'
202Accepted
{
  "job_id": "job_abc123",
  "status": "accepted",
  "poll_url": "/v1/jobs/job_abc123",
  "request_id": "req_abc123",
  "message": "Scraper job accepted. Poll poll_url for the result."
}

Poll

Wait 3 seconds after the 202, then wait the retry_after_ms of each response (currently 3000). Stop polling after expires_at. Never poll the same job in parallel. Responses may carry extra fields: ignore keys you do not know.

The person has about 2 minutes to approve. A late approval ends as AT-2001 (timed out, not billed): resubmit. Some institutions ask for two approvals.

200Running
{
  "job_id": "job_abc123",
  "status": "running",
  "request_id": "req_abc123",
  "poll_url": "/v1/jobs/job_abc123",
  "retry_after_ms": 3000,
  "expires_at": "2026-11-03T05:08:00.000Z"
}
200Done
{
  "status": "done",
  "data": { "result": { "lists": [ { "...": "..." } ] } },
  "meta": {
    "cached": false,
    "latency_ms": 41250,
    "credits_used": 60,
    "request_id": "req_abc123",
    "provider": "apitree-scraper"
  }
}
422Failed
{
  "status": "failed",
  "error": "AT-2001",
  "http_status": 422,
  "message": "<customer message>",
  "recovery_action": "<what to do next>",
  "docs_url": "https://apitree.ai/docs/errors",
  "request_id": "req_abc123",
  "error_category": "network",
  "billable": false
}

Status and next action

Response
Next action
202Accepted. Poll again in 3 seconds.
200 runningWait retry_after_ms, then poll again.
200 doneUse data. Finished.
422 failedFollow recovery_action. If billable is true, retrying the same input is billed.
410 JOB_EXPIREDStop and resubmit. Refunded if the job never finished. If the result of a finished job aged out (15 minutes), the original charge stands.
504 JOB_TIMEOUTStop and resubmit.
404 JOB_NOT_FOUNDCheck the job_id.
403 FORBIDDENPoll with a key of the same organization.
502 SCRAPER_UNREACHABLE / SCRAPER_MALFORMEDPoll step. Temporary. Keep polling.
503 SCRAPER_UNAVAILABLERetry later.
400 INVALID_PARAMETER / VARIANT_UNAVAILABLESubmit step. Fix the input. Not billed.
402 INSUFFICIENT_CREDITSSubmit step. Top up credits.
403 ORG_CONTEXT_REQUIREDSubmit step. Use an organization key.
502 SCRAPER_UNREACHABLESubmit step. The reserved credits are refunded. Retry.
502 JOB_CREATE_FAILEDSubmit step. The reserved credits are refunded. Retry.

Billing

The price is reserved when the job is accepted (402 if the balance is short). A successful done keeps the charge, once, however often you poll again. A 422 with billable true is charged because the institution gave a definitive answer. Every other 422 and every 504 is refunded. A 410 is refunded when the job expired before it finished. A 410 returned after a finished job's result passed its 15-minute retention window keeps the original charge, because the result was already delivered and billed.

A job that does not finish within 8 minutes of submission is expired and refunded by a cleanup task, whether or not you polled it, and its result cannot be retrieved. Poll promptly and collect the result as soon as it is done. See Error Codes.

Expired and timed out

Polling a job that already ended returns a message saying it already terminated. Both responses below are final: resubmit the original request.

410JOB_EXPIRED
{
  "error": "JOB_EXPIRED",
  "http_status": 410,
  "message": "Result expired (15min TTL). Resubmit the request.",
  "recovery_action": "The async job result is no longer available (expired, or already delivered on a prior poll). Re-submit the original request to get a fresh job_id.",
  "docs_url": "https://apitree.ai/docs/errors#job-expired",
  "request_id": "req_abc123"
}
504JOB_TIMEOUT
{
  "error": "JOB_TIMEOUT",
  "http_status": 504,
  "message": "Job did not finish within 8 minutes. Resubmit the request.",
  "recovery_action": "Stop polling this job_id and resubmit the original request to create a new job. If this recurs, the institution site may be slow or the worker degraded.",
  "docs_url": "https://apitree.ai/docs/errors#job-timeout",
  "request_id": "req_abc123"
}

SDK and MCP

The TypeScript SDK polls for you after a 202. In a serverless function pass waitForAuth: false and call waitForJob later. With MCP, call_api returns auth_pending and a job_id; poll it with poll_job.

tsSDK
const result = await apitree.call("{slug}", body, {
  method: "POST",
  pollTimeoutMs: 300_000, // the SDK polls for you after a 202
});
textMCP
call_api  -> status: "auth_pending" + job_id
poll_job  -> call every 3-5 s until status is no longer "running"
에피트리(apitree) · AI-Native API Marketplace