Bulk Search API
Stream up to 20 independent eBay searches through one authenticated CompSniper request.
POST /v1/bulk-search accepts up to 20 unique keywords and streams each independent result as soon as it
finishes. The server runs at most three searches concurrently.
Start a stream
curl -N --fail-with-body \
-X POST "https://api.compsniper.com/v1/bulk-search" \
-H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Idempotency-Key: inventory-price-run-2026-09-02" \
-d '{
"keywords": [
"Apple iPhone 14 128GB",
"Shure SM7B",
"Garmin 8612xsv"
],
"options": {
"count": 60,
"ebaySite": "ebay.com",
"relevance": true,
"exactMatch": false
}
}'Keywords are trimmed and deduplicated case-insensitively. Every keyword searches page 1 using the same
options. Supported options match the main search endpoint except page, because each streamed result is
one independently metered page.
Stream events
The response uses Content-Type: text/event-stream and emits:
start: run ID, total unique keywords, effective concurrency, replay state, combined CSV URL, and billing behavior.result: one success or failure with its original inputindexandkeyword. Successful rows include a per-keyword CSV URL.complete: run ID, terminal status, total, succeeded and failed counts, replay state, and combined CSV URL.
Results can finish out of order. Use index to restore the submitted order.
event: start
data: {"runId":"RUN_ID","total":3,"allocated":3,"quotaSkipped":0,"concurrency":3,"replayed":false,"combinedCsvUrl":"/v1/bulk-search/RUN_ID/download.csv"}
event: result
data: {"index":1,"keyword":"Shure SM7B","ok":true,"status":200,"response":{"totalItems":60},"csvUrl":"/v1/bulk-search/RUN_ID/download.csv?index=1"}
event: complete
data: {"runId":"RUN_ID","total":3,"succeeded":3,"failed":0,"status":"done","replayed":false}Billing and failures
Before starting, the endpoint determines how many keywords fit the account's selected monthly and credit
balance. If at least one fits, those first keywords run and every remaining keyword receives its own
429 quota_exceeded result event. The start event reports allocated and quotaSkipped. If none fit,
the HTTP request returns 429 without creating a run.
Each successful allocated keyword uses monthly quota first, then one purchased credit, and includes
remaining monthly and credit balances in the result event. Add
X-Credit-Source: credits to fund the entire run from purchased credits. A failed keyword uses the same
error contract as GET /v1/scrape and is not charged.
Allocation always follows input order. A later keyword is never run ahead of an earlier keyword merely
because results complete out of order. Quota-skipped keywords are persisted with the run, replay with the
same Idempotency-Key, and are excluded from CSV exports like other failed results.
Reuse the same Idempotency-Key and identical JSON body after a disconnect. Once the original run is
terminal, CompSniper replays the stored start, result, and complete events without repeating searches or
consuming quota. Reusing the key with a different body returns 409. Retrying while the original run is
still active also returns 409, so wait briefly and retry the same key.
Run results and CSV exports remain available for 30 days.
Download CSV
The start and complete events include the combined CSV URL. Each successful result event includes a
URL for only that keyword. Send the same account's bearer credential when downloading:
curl --fail-with-body \
-H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
"https://api.compsniper.com/v1/bulk-search/RUN_ID/download.csv" \
-o compsniper-bulk.csvcurl --fail-with-body \
-H "Authorization: Bearer cs_live_REPLACEWITHYOURKEY" \
"https://api.compsniper.com/v1/bulk-search/RUN_ID/download.csv?index=1" \
-o shure-sm7b.csvSold and active bulk searches receive the appropriate CSV columns. Failed keywords are excluded from the combined export.
Node.js event reader
const response = await fetch("https://api.compsniper.com/v1/bulk-search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.COMPSNIPER_API_KEY}`,
"Content-Type": "application/json",
Accept: "text/event-stream",
},
body: JSON.stringify({
keywords: ["Apple iPhone 14 128GB", "Shure SM7B"],
options: { count: 60, relevance: true },
}),
});
if (!response.ok) throw new Error(await response.text());
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const frames = buffer.split("\n\n");
buffer = frames.pop() ?? "";
for (const frame of frames) {
const type = frame.match(/^event: (.+)$/m)?.[1];
const raw = frame.match(/^data: (.+)$/m)?.[1];
if (type && raw) console.log(type, JSON.parse(raw));
}
}