Get data¶
Requires a valid JWT unless noted. See Authentication.
Set a shell variable for examples:
Projects¶
List projects¶
GET /projects — Query: optional archived (true | false).
Success
{
"data": [
{
"id": "67d1...",
"name": "My project",
"organizationId": "67c...",
"archived": false,
"createdAt": "2025-03-01T12:00:00.000Z",
"updatedAt": "2025-03-01T12:00:00.000Z"
}
]
}
Create project¶
POST /projects — Body: { "name": string }.
const r = await fetch(`${process.env.API_BASE}/projects`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "EU keywords" }),
});
const body = await r.json();
if (!r.ok) console.error(body);
else console.log(body.data.id);
Success
{
"data": {
"id": "67d2ab...",
"name": "EU keywords",
"organizationId": "67c...",
"archived": false,
"createdAt": "2025-03-20T10:00:00.000Z",
"updatedAt": "2025-03-20T10:00:00.000Z"
}
}
Error (validation, HTTP 400)
Update project¶
PATCH /projects/:id — Body: optional name, archived.
curl -sS -X PATCH "$API_BASE/projects/67d2ab..." \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"archived":true}'
Crawl jobs¶
Create job¶
POST /projects/:projectId/jobs
Body:
| Field | Type | Notes |
|---|---|---|
type |
"search" | "details" |
Search uses keywords; details uses ASINs or /dp/ASIN URLs. |
targets |
string[] |
Non-empty. For details, each target must resolve to a 10-char ASIN. |
config |
object (optional) | e.g. { "lang": "en-US" }; extra keys allowed. |
const projectId = process.env.PROJECT_ID;
const r = await fetch(`${process.env.API_BASE}/projects/${projectId}/jobs`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "search",
targets: ["wireless earbuds"],
config: { lang: "en-US" },
}),
});
console.log(await r.json());
Success
{
"data": {
"id": "67e3...",
"projectId": "67d2ab...",
"organizationId": "67c...",
"type": "search",
"status": "queued",
"targets": [{ "value": "wireless earbuds", "status": "pending" }],
"config": { "lang": "en-US" },
"createdAt": "2025-03-20T10:05:00.000Z",
"updatedAt": "2025-03-20T10:05:00.000Z",
"bullJobId": "67e3..."
}
}
Error (quota, HTTP 403)
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Job limit reached for this month",
"details": []
}
}
List jobs for a project¶
GET /projects/:projectId/jobs
Query:
| Param | Default | Notes |
|---|---|---|
status |
— | Filter by job status string. |
limit |
20 |
Capped at 100 in the repository. |
offset |
0 |
Pagination offset. |
curl -sS "$API_BASE/projects/$PROJECT_ID/jobs?limit=10&offset=0&status=completed" \
-H "Authorization: Bearer $TOKEN"
Success
{
"data": [ { "id": "67e3...", "status": "completed", "type": "search" } ],
"meta": { "total": 42 }
}
Get job by id¶
GET /jobs/:id
Success
{
"data": {
"id": "67e3...",
"projectId": "67d2ab...",
"organizationId": "67c...",
"type": "search",
"status": "completed",
"targets": [{ "value": "wireless earbuds", "status": "success" }],
"config": { "lang": "en-US" },
"resultSetId": "67e4...",
"createdAt": "...",
"updatedAt": "...",
"completedAt": "..."
}
}
status is one of: queued, running, completed, failed, cancelled.
Not found (HTTP 404)
Get results¶
GET /jobs/:id/results — Returns stored result rows for the job (items array shape depends on crawl output).
Success
The objects inside data are whatever the crawler persisted for that job type; treat them as opaque JSON until you standardize on a schema in your integration.
Export job results¶
GET /jobs/:id/export — Query: format = json (default) or csv. Response is a file download (Content-Disposition: attachment), not a JSON envelope.
const r = await fetch(
`${process.env.API_BASE}/jobs/${process.env.JOB_ID}/export?format=json`,
{ headers: { Authorization: `Bearer ${process.env.TOKEN}` } },
);
if (!r.ok) {
console.error(await r.json());
} else {
const buf = Buffer.from(await r.arrayBuffer());
console.log(buf.toString("utf8").slice(0, 200));
}
Error (HTTP 4xx/5xx) — JSON error envelope from the filter when the use case throws (e.g. not found).
Retry job¶
POST /jobs/:id/retry
Response: { "data": { ...job } } — a new queued job copying the original type, config, and target values (targets reset to pending).
Cancel job¶
POST /jobs/:id/cancel
Schedules¶
| Method | Path | Body / notes |
|---|---|---|
POST |
/projects/:projectId/schedules |
{ "cron": "<cron string>", "crawlConfig": { "type", "targets", "config?" } } |
GET |
/projects/:projectId/schedules |
List schedules for the project. |
PATCH |
/schedules/:id |
Optional enabled, cron, crawlConfig. |
Cron handling in the service targets daily-style expressions; validate in your environment before relying on edge cases.
Usage¶
GET /usage — Monthly limits vs usage for the JWT’s organization.
{
"data": {
"limits": { "jobsPerMonth": 10, "targetsPerMonth": 100, "exportsPerMonth": 20 },
"used": { "jobsThisMonth": 3, "targetsThisMonth": 40, "exportsThisMonth": 2 },
"remaining": { "jobsPerMonth": 7, "targetsPerMonth": 60, "exportsPerMonth": 18 }
}
}
Health (no auth)¶
GET /health
Quick reference¶
| Endpoint | Method | Auth |
|---|---|---|
/health |
GET | N |
/auth/register |
POST | N |
/auth/login |
POST | N |
/auth/login-handoff |
POST | N |
/auth/verify-handoff |
POST | N |
/auth/logout |
POST | N (Bearer optional; stateless JWT) |
/projects |
GET | Y |
/projects |
POST | Y |
/projects/:id |
PATCH | Y |
/projects/:projectId/jobs |
POST | Y |
/projects/:projectId/jobs |
GET | Y |
/jobs/:id |
GET | Y |
/jobs/:id/results |
GET | Y |
/jobs/:id/export |
GET | Y |
/jobs/:id/retry |
POST | Y |
/jobs/:id/cancel |
POST | Y |
/projects/:projectId/schedules |
POST, GET | Y |
/schedules/:id |
PATCH | Y |
/usage |
GET | Y |
/audit |
GET | Y |
/referral/* |
various | see Referral API |
/admin/* |
various | Admin JWT |
See also Webhooks and Authentication.