Agent integration guide
Everything an agent needs to call paid endpoints through Sourcelane: one authenticated POST, an itemised receipt, and a flat set of guardrail error codes to handle.
Getting an API key
Agent keys are created from the dashboard, not this page — sign in, go to Dashboard → Agents, and create an agent. The plaintext key (sl_live_…) is shown exactly once at creation time; only its hash is stored server-side. Set spend caps (daily limit, monthly limit, max transaction size, allowlist) on the same agent before you use it — a key with no caps set is not a safe default.
Send the key as a bearer token on every gateway request: Authorization: Bearer sl_live_….
MCP server & AI platforms
Sourcelane runs a remote MCP server (Streamable HTTP) at https://api.usesourcelane.com/mcp. Authenticate with the same agent key as the REST API. It exposes four tools: search_connectors, get_connector, call_connector and get_account — so your AI can find the right connector, read its params and run it without any bespoke integration.
Connect your tools
Pick your tool and connect in under a minute. Then just ask — for example: “Use Sourcelane to run instagram-profile-scraper and summarise what you find.”
Connect Claude
Web, desktop and mobile. Paste one URL.
- 1Copy your personal connector URL
- 2In Claude open Settings → Connectors → Add custom connector
- 3Paste the URL and click Add — done
Manual setup (config files, REST, Python) +
Claude Code
Adds the Sourcelane MCP server with your key as a header.
claude mcp add --transport http sourcelane https://api.usesourcelane.com/mcp \
--header "Authorization: Bearer sl_live_your_agent_key"Claude Desktop (config file)
Alternative to the connector URL: add to claude_desktop_config.json, then restart Claude.
{
"mcpServers": {
"sourcelane": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://api.usesourcelane.com/mcp",
"--header",
"Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer sl_live_your_agent_key"
}
}
}
}Cursor & Windsurf
Add to ~/.cursor/mcp.json (or Windsurf's mcp_config.json).
{
"mcpServers": {
"sourcelane": {
"url": "https://api.usesourcelane.com/mcp",
"headers": {
"Authorization": "Bearer sl_live_your_agent_key"
}
}
}
}VS Code
Save as .vscode/mcp.json. VS Code prompts for your key once.
{
"servers": {
"sourcelane": {
"type": "http",
"url": "https://api.usesourcelane.com/mcp",
"headers": {
"Authorization": "Bearer ${input:sourcelane-key}"
}
}
},
"inputs": [
{
"type": "promptString",
"id": "sourcelane-key",
"description": "Sourcelane agent key",
"password": true
}
]
}ChatGPT Custom GPT (Actions)
Create a GPT → Actions → Import from URL, then Authentication: API Key, Bearer.
https://usesourcelane.com/openapi.jsonREST
One POST. Pass maxResults to cap cost.
curl -X POST https://api.usesourcelane.com/v1/call \
-H "Authorization: Bearer sl_live_your_agent_key" \
-H "Content-Type: application/json" \
-d '{"listing":"instagram-profile-scraper","params":{"usernames":["natgeo"]},"maxResults":10}'Python, LangChain, CrewAI, OpenAI Agents SDK…
Wrap the REST call as a tool, or point an MCP client at the endpoint.
import requests
res = requests.post(
"https://api.usesourcelane.com/v1/call",
headers={"Authorization": "Bearer sl_live_your_agent_key"},
json={
"listing": "instagram-profile-scraper",
"params": {"usernames":["natgeo"]},
"maxResults": 10,
},
timeout=300,
)
body = res.json()
print(body["receipt"]["chargedMicros"], "micro-USD for", body["receipt"]["results"], "results")
print(body["data"])Per-result pricing
Most connectors are priced per result (per profile, per post, per review…). Each call first reserves the listing's per-call ceiling — the unit price times the most results one call can return — then settles at the number of results actually returned and refunds the rest in the same second. A call that returns nothing costs nothing.
Pass maxResults alongside params to lower both the ceiling and the reservation. The receipt shows reservedMicros, chargedMicros, refundedMicros and results.
A connector call can take up to a few minutes for large requests — set your HTTP client timeout to at least 300s.
POST /v1/call
The single endpoint an agent calls to use a listing. Body carries the listing's slug and a params object matching that listing's published request schema. Sourcelane resolves the listing, evaluates guardrails, debits the ledger, forwards the call upstream, and settles or refunds based on the result — all in one round trip.
curl -X POST "https://api.usesourcelane.com/v1/call" \
-H "Authorization: Bearer sl_live_your_agent_key" \
-H "Content-Type: application/json" \
-d '{
"listing": "global-weather-now",
"params": { "latitude": 52.52, "longitude": 13.41 }
}'On success (200):
{
"requestId": "req_8fK2n...",
"listing": "global-weather-now",
"data": { /* verbatim upstream JSON */ },
"receipt": {
"priceMicros": 2000,
"chargedMicros": 2000,
"balanceAfterMicros": 9998000,
"latencyMs": 412,
"upstreamLatencyMs": 388,
"x402": {
"network": "base",
"asset": "USDC",
"txRef": "sim_9c1e...",
"simulated": true
}
}
}On a blocked or failed call:
{
"requestId": "req_8fK2n...",
"error": {
"code": "daily_limit_exceeded",
"message": "This call would exceed the agent's daily spend limit."
}
}GET /v1/listings
Returns the live, browsable listings — the same set backing the public directory — so an agent can discover endpoints without a human in the loop. No body; the key on the request just needs to be valid.
curl "https://api.usesourcelane.com/v1/listings" \
-H "Authorization: Bearer sl_live_your_agent_key"GET /v1/me
Returns the calling agent's own state: status, kill switch, guardrail configuration, and current spend against each limit. Useful for an agent to self-check before it spends, rather than discovering a limit by hitting it.
curl "https://api.usesourcelane.com/v1/me" \
-H "Authorization: Bearer sl_live_your_agent_key"Guardrail error codes
Guardrails are evaluated in a fixed order — the first one that fails wins, and every field below except invalid_key is recorded as a blocked call (and never charged).
| code | HTTP status | Meaning |
|---|---|---|
invalid_key | 401 | No key, or the key doesn't match any agent. Not recorded as a call — there's no agent to attach it to. |
agent_revoked | 403 | The agent's key has been rotated or the agent removed. |
agent_paused | 403 | The agent is paused from the dashboard. |
kill_switch | 403 | The agent's kill switch is on. Takes effect immediately. |
listing_not_found | 404 | No listing matches the slug you sent. |
listing_not_live | 404 | The listing exists but isn't in the live status yet. |
not_allowlisted | 403 | The agent's allowlistMode is "allowlist" and this listing isn't on it. |
max_tx_exceeded | 403 | The listing's price is above this agent's maxTxMicros. |
daily_limit_exceeded | 403 | This call would put the agent over its daily spend limit. |
monthly_limit_exceeded | 403 | This call would put the agent over its monthly spend limit. |
total_budget_exceeded | 403 | This call would put the agent over its lifetime budget. |
insufficient_balance | 402 | The account balance can't cover the listing's price. |
Refunds on failure
Calls are reserve-then-settle. Authorizing a call debits the ledger and opens a calls row immediately; the upstream request only runs after that. If the upstream call doesn't come back as a success — an upstream error, a timeout, or a failed x402 payment — the debit is automatically reversed. You only ever end up charged for a call that actually returned data.
Guardrail blocks (an unmet limit, an unlisted endpoint, and so on) are rejected before any debit happens at all, so there's nothing to refund on those.
Every post-reservation failure answers 424 Failed Dependency — not 502, because the CDN in front of the gateway rewrites the body of gateway-class 5xx responses and would strip this envelope. Branch on error.code, never on the status alone:
| Code | Status | Meaning |
|---|---|---|
bad_request | 400 | Malformed body — `listing` missing, or `params` isn't an object. |
payment_failed | 424 | x402 settlement was rejected before the upstream was called. |
upstream_unreachable | 424 | The listing's endpoint could not be reached at all. |
upstream_error | 424 | The endpoint answered with a non-2xx status. |
invalid_upstream_response | 424 | The endpoint answered, but not with parseable JSON. |
upstream_timeout | 424 | The endpoint did not respond within 20 seconds. |
All of the above are refunded automatically, and their messages say so explicitly.
Code samples
Same request, three languages.
curl -X POST "https://api.usesourcelane.com/v1/call" \
-H "Authorization: Bearer sl_live_your_agent_key" \
-H "Content-Type: application/json" \
-d '{
"listing": "global-weather-now",
"params": { "latitude": 52.52, "longitude": 13.41 }
}'const res = await fetch("https://api.usesourcelane.com/v1/call", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SOURCELANE_AGENT_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
listing: "global-weather-now",
params: { latitude: 52.52, longitude: 13.41 },
}),
});
const result = await res.json();
if (!res.ok) {
// result.error = { code, message }
throw new Error(`${result.error.code}: ${result.error.message}`);
}
console.log(result.data); // upstream JSON, verbatim
console.log(result.receipt); // priceMicros, chargedMicros, balanceAfterMicros, latencyMs, x402import os, requests
res = requests.post(
"https://api.usesourcelane.com/v1/call",
headers={"Authorization": f"Bearer {os.environ['SOURCELANE_AGENT_KEY']}"},
json={"listing": "global-weather-now", "params": {"latitude": 52.52, "longitude": 13.41}},
)
result = res.json()
if not res.ok:
raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
data = result["data"]
receipt = result["receipt"]Base URL used above: https://api.usesourcelane.com. Questions about a specific listing's request/response schema? Check that listing's page in the directory.