Integrate looot into your app
Call any looot job or tool from your own backend with one HTTP request. API key, first call, reading the answer, retries, long runs, files and costs.
Your app can use every looot job and tool directly, without an AI agent: a CRM that finds work emails for new leads, an internal dashboard that enriches companies, a pipeline that pulls SEO rankings every night. It is one REST API with one key and one prepaid balance.
1. Get an API key
In the dashboard, open Settings, API keys and create a key for your app. Give it a monthly spending limit so a bug in your code can never spend your whole balance. Keep the key on your server, never in a browser or mobile app.
export LOOOT_TOKEN="your key"
2. Pick a job or a tool
- A job is a task, like
people.email.find. looot picks a provider for you, and withfallbackon it tries the next one if the first finds nothing. Start here. - A tool is one provider’s endpoint, like
hunter-email-finder. Use it when you need that exact provider.
Browse them all in Jobs and Tools. Each page lists the inputs, the providers and their prices, and ready code.
3. Make the call
One request starts the run and, with wait, returns the answer in the same response.
curl -X POST "https://api.looot.ai/v1/runs" \
-H "Authorization: Bearer $LOOOT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: lead-4821-email" \
-d '{"endpointId":"job:people.email.find","input":{"first_name":"Jane","last_name":"Doe","domain":"example.com"},"fallback":true,"wait":30}'
const response = await fetch("https://api.looot.ai/v1/runs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LOOOT_TOKEN}`,
"Content-Type": "application/json",
"Idempotency-Key": `lead-${lead.id}-email`,
},
body: JSON.stringify({
endpointId: "job:people.email.find",
input: { first_name: lead.firstName, last_name: lead.lastName, domain: lead.domain },
fallback: true,
wait: 30,
}),
});
const run = await response.json();
import os, requests
run = requests.post(
"https://api.looot.ai/v1/runs",
headers={
"Authorization": f"Bearer {os.environ['LOOOT_TOKEN']}",
"Idempotency-Key": f"lead-{lead_id}-email",
},
json={
"endpointId": "job:people.email.find",
"input": {"first_name": "Jane", "last_name": "Doe", "domain": "example.com"},
"fallback": True,
"wait": 30,
},
timeout=70,
).json()
4. Read the answer
| Field | What it holds |
|---|---|
status |
completed, failed, or queued and running while it works |
outcome |
hit (found it), weak, miss (nothing found), error, rejected (bad input) |
normalized |
The answer in looot’s own field names, the same whichever provider answered (for example normalized.email) |
result |
The provider’s raw answer, as it sent it |
pricing.finalChargeMicros |
What this run cost, in millionths of a dollar |
route |
With fallback: every provider tried, in order, and which one answered |
Read normalized first. It doesn’t change when looot switches provider.
5. Retries never pay twice
Send the same Idempotency-Key again and you get the same run back, never a second charge. Build
the key from your own record (lead-4821-email), so a crashed worker can safely retry.
6. Long runs
wait holds the request for up to 60 seconds. If the run takes longer, you get it back with
status: "queued" or "running". Poll it:
curl "https://api.looot.ai/v1/runs/RUN_ID" -H "Authorization: Bearer $LOOOT_TOKEN"
7. Files and text answers
Some tools answer with a file (speech, an image, a video, a PDF) or with CSV or plain text.
- Small text and CSV answers come back in
result.text, withresult.format(csv,text,html,xml,markdown). - Files and large answers come back as
result.media, with adownloadPath. Download it with your key:GET https://api.looot.ai+downloadPath.
8. What it costs
- Every run reserves its maximum price first, then charges what the provider really billed. The rest goes back to your balance.
- A run that fails at the provider costs $0. A job run that finds nothing usually costs $0.
- Check your balance with
GET /v1/balanceand top up in the dashboard.
9. Errors to handle
| HTTP | Code | What to do |
|---|---|---|
| 402 | insufficient_balance |
Top up, then retry with a new idempotency key |
| 402 | key_spend_limit_reached |
This key hit its monthly limit. Raise it in Settings, API keys |
| 400 | validation_error |
The input doesn’t match the tool. error.details names the field |
| 429 | too_many_inflight_runs |
Slow down, then retry |
Every error has error.code, error.message and error.retryable.
Write actions
Some tools change data in an account you connected (send an email, create a CRM contact, delete a
file). Their pages say Write action at the top, and GET /v1/operations/{id} returns
effectClassification: "effectful". Call those only on purpose.