Lokal mit eigenen Skripten
Lokal mit eigenen Skripten
Local mode is for people who run their own scripts against the Skael API, for example with Claude Code or Cursor on their own machine. Three building blocks make that safe and cheap to retry: a batch endpoint for up to 1,000 rows per request, an Idempotency-Key header that keeps a retried request from being charged twice, and ready code recipes from the Skael MCP.
All requests use your API key in the Authorization header, exactly like every other endpoint. See Authentication.
Batch endpoint
POST /api/enrichments/<type>/batch
Send up to 1,000 rows of the same endpoint in one request. Every row becomes its own enrichment with the same price, credit reservation and deduplication as a single request. The answer comes at once with a batch id; the rows run in the background.
curl -X POST https://api.skael.de/api/enrichments/verify_email/batch \
-H "Authorization: Bearer eak_your_api_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: newsletter-2026-10-03-part-1" \
-d '{
"rows": [
{"email": "anna@example.com", "custom_vars": {"crm_id": "A-1"}},
{"email": "ben@example.com"}
],
"callback_url": "https://yourapp.example/hooks/skael"
}'
| Field |
Required |
Description |
rows |
one of rows or csv_url |
1 to 1,000 objects, each one the input of the endpoint. A row may carry its own custom_vars. |
csv_url |
one of rows or csv_url |
An https link to a CSV or XLSX file with a header row, at most 1,000 data rows. Columns named like the endpoint's input fields are used as they are. |
mapping |
no |
With csv_url: rename columns, for example {"E-Mail": "email"}. |
custom_vars |
no |
Default custom_vars for rows without their own. |
callback_url |
no |
Called once when the whole batch has finished. |
The answer is 202 Accepted:
{
"id": 812,
"object": "batch",
"type": "verify_email",
"status": "processing",
"counts": {"total": 2, "pending": 2, "queued": 0, "processing": 0, "completed": 0, "failed": 0, "rejected": 0},
"credits_used": 0,
"estimated_credits": 6,
"rejected": [],
"links": {
"self": "https://api.skael.de/api/batches/812",
"results_csv": "https://api.skael.de/api/batches/812/results.csv"
}
}
Rows that fail the input check are listed under rejected right away and cost nothing; the other rows run. If your credits run out halfway, the rows that could not be paid are reported as rejected with the code insufficient_credits; everything before them is delivered.
Progress and results
GET /api/batches/<id>?page=1&per_page=100
Returns the counts and one page of results (per_page up to 500). status turns to completed when no row is left open. Every result row has its position in your input (row, starting at 0), its status (pending, queued, processing, completed, failed or rejected), the enrichment_id, your input and custom_vars, the output, the credits it cost and an error with code and message when it did not work.
GET /api/batches/<id>/results.csv
The same results as one CSV file: your input columns, custom_vars.*, the output as flat output.* columns, then credits, error_code and error_message.
Callback
With a callback_url, Skael sends one POST when the batch has finished. The body is the batch summary (as in GET /api/batches/<id> without the result rows) with "event": "batch.completed". Fetch the rows from links.self or links.results_csv. The same rules as for single callbacks apply: public https or http address, 10 second timeout, redirects are not followed. A failed delivery is retried three times over about 12 minutes.
Limits
- 1,000 rows per batch; split larger files into several batches.
- 10 unfinished batches per organization at a time, 30 new batches per minute. Above that the API answers
429.
- At most 100 rows of one batch run at the same time. Batches never slow down your lead list runs; they share the queue with your other asynchronous single requests.
Idempotency-Key
Network errors happen: a request times out, but did it arrive? Send an Idempotency-Key header with every POST to /api/enrichments/<type> and /api/enrichments/<type>/batch, and retry with the same key:
- Same key, same body within 24 hours: you get the original enrichment or batch back, with the header
Idempotent-Replayed: true. Nothing is charged again.
- Same key, different body:
409 Conflict with the code idempotency_key_reused. Use a new key for a new request.
- Same key while the first request is still being accepted:
409 Conflict with the code idempotency_key_in_flight and Retry-After: 2. Wait and retry.
A key is up to 255 printable characters without spaces and belongs to your organization. A request that was refused (for example 402 for missing credits or 422 for invalid input) does not use up its key; fix the cause and send it again with the same key.
A good key is derived from the row itself, for example a hash of your run name, the row number and the row's content. Then a script that restarts after a crash sends exactly the same keys again.
Code recipes from the MCP
If you work with Claude Code, Cursor or another assistant that has the Skael MCP connected, ask it for a script:
Hol mir mit get_code_recipe ein Python-Skript für verify_email und lass es auf kunden.csv laufen.
The tool get_code_recipe returns a ready, commented Python script for one endpoint. It comes from a versioned template, not from a language model, and uses only the Python standard library:
mode: "batch" (default) sends the CSV in batches of up to 1,000 rows; mode: "single" sends one request per row, a few at a time.
- It submits asynchronously and checks for results with a growing pause, or passes your
--callback-url.
- It keeps its progress in
<output>.state.json, written before the first request. Stop it at any time and run the same command again; every batch or row carries its Idempotency-Key, so nothing is charged twice.
- Rows that were not run because credits or a spend limit ran out are sent again on the next start of the same command; rows that already finished are not sent again. Top up first, then restart.
- The batch script keeps at most 10 batches open at the same time (the limit per organization) and sends the next one as soon as one has finished.
- It reads a CSV (comma or semicolon) and writes one CSV with your columns plus
skael_status, skael_credits, skael_error and the result as out.* columns.
export SKAEL_API_KEY=eak_your_api_key_here
python3 skael_verify_email_batch.py kunden.csv kunden_geprueft.csv
Try a file with three to five rows first.
Upload a file
Lists that are too big to paste into a chat can be uploaded. The MCP tool create_upload returns a signed upload_url (valid for 30 minutes) and a csv_url (valid for 7 days). Upload the file with PUT:
curl -X PUT -H "content-type: text/csv" --upload-file kunden.csv "<upload_url>"
Then use the csv_url as csv_url of the batch endpoint, in a lead list (draft_pipeline) or as a match list (add_match_list). CSV and XLSX files up to 20 MB are accepted. A lead list reads the file again at the start of every run, so a run that starts after the 7 days needs a new upload.