Skip to content

Quickstart

Verify one address or a larger list with the same flow: create a verification, upload a CSV, then read saved progress and results.

Copy your key from the dashboard and store it as AGENTS_VERIFY_API_KEY in your terminal or server environment.

Terminal window
curl --fail-with-body 'https://api.agentsverify.dev/v1/credits' \
--header "X-API-Key: ${AGENTS_VERIFY_API_KEY:?Set your API key}"

The credits value is your available balance. This read does not reserve credits. For current file-size and row limits, call GET /v1/verifications.

Create a one-column UTF-8 file named contacts.csv, with an explicit email header and your addresses below it:

email
person@example.com

The address above is a format example. Use your own addresses for real verification. Standard CSV quoting, an optional initial BOM and LF or CRLF are supported. Blank records, extra columns or malformed addresses reject the whole file before checks or credit holds. Duplicates are checked once.

Generate a UUID and save it as REQUEST_ID before the first request. Reuse it when recovering that same submission.

Creation is a bodyless POST. Compute the byte length from the actual file:

Terminal window
curl --request POST 'https://api.agentsverify.dev/v1/verifications' \
--fail-with-body \
--header "X-API-Key: ${AGENTS_VERIFY_API_KEY:?Set your API key}" \
--header "Idempotency-Key: ${REQUEST_ID:?Set and save a UUID first}" \
--header "X-Upload-Content-Length: $(wc -c < contacts.csv)" \
--header "X-Upload-Filename: contacts.csv"

HTTP 201 creates a verification; HTTP 200 recovers it. Save the returned id. When the receipt contains upload, PUT the CSV bytes to upload.url without API credentials.

This Node.js example performs both stages. Save it as upload.mjs and run node upload.mjs contacts.csv with both environment variables set:

async function uploadCsv(filePath, apiKey, idempotencyKey) {
const {readFile} = await import('node:fs/promises');
const {basename} = await import('node:path');
if (!apiKey || !idempotencyKey) {
throw new Error('Set AGENTS_VERIFY_API_KEY and your saved REQUEST_ID');
}
const csv = await readFile(filePath);
if (csv.length === 0) throw new Error('The CSV file is empty');
const filename = encodeURIComponent(basename(filePath));
const created = await fetch('https://api.agentsverify.dev/v1/verifications', {
method: 'POST', redirect: 'manual',
headers: {
'X-API-Key': apiKey,
'Idempotency-Key': idempotencyKey,
'X-Upload-Content-Length': String(csv.length),
'X-Upload-Filename': filename
}
});
const receipt = await created.json();
if (created.status !== 200 && created.status !== 201) {
throw new Error(receipt.issue?.message || 'API request rejected');
}
if (receipt.upload) {
const stored = await fetch(receipt.upload.url, {
method: 'PUT', body: csv, credentials: 'omit', redirect: 'manual',
headers: {
'Content-Type': 'text/csv',
'Content-Range': 'bytes 0-' + (csv.length - 1) + '/' + csv.length,
'X-Goog-Meta-Original-Filename': filename
}
});
if (stored.status === 308) {
throw new Error('Upload incomplete: resume the saved session');
}
if (stored.status !== 200 && stored.status !== 201) {
throw new Error('Storage rejected the upload (HTTP ' + stored.status + ')');
}
}
return receipt.id;
}
const verificationId = await uploadCsv(
process.argv[2] || 'contacts.csv',
process.env.AGENTS_VERIFY_API_KEY,
process.env.REQUEST_ID
);
console.log('Verification ID:', verificationId);

The example transfers a whole file. It does not resume incomplete transfers. Storage HTTP 200 or 201 confirms receipt. After confirmation, the job can briefly remain uploading while the backend detects completion. Continue polling instead of re-uploading.

Poll GET /v1/verifications/{id} and respect Retry-After. Read issue for actionable guidance. Whole-file validation and processing start automatically; no completion or batching call is needed.

Download GET /v1/verifications/{id}/results for finalized CSV outcomes. Partial results contain finished checks; a header-only CSV means none are available yet. Read saved results again without submitting another verification.

Continue with the complete workflow, results and credits or endpoint reference.

Send an empty PUT to the same private upload.url with Content-Range: bytes */<total bytes>. HTTP 200 or 201 means complete. A 308 response identifies persisted bytes in Range. Continue at the next byte, or byte zero if no Range is present, following the storage resumption protocol.

Keep omitting API credentials from storage requests. Recover an uncertain API creation using the original Idempotency-Key; a new identity creates new work and can charge again.