API reference
One endpoint.
Send a question and a starting URL. Get back the passages that answer it, each with the page and the block it came from. Everything below is the whole surface.
Base URL
https://api.aipinto.com
Quickstart
Authenticate with a key in the Authorization header. Keys are issued by hand
during the alpha, so ask for one
and it arrives with credit already on the account.
curl -X POST https://api.aipinto.com/v1/research \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: job-1234" \
-d '{
"query": "How do I read a file line by line in Python?",
"url": "https://docs.python.org/3/tutorial/",
"maxPages": 4,
"maxDepth": 2,
"maxCost": 0.05
}'
{
"result": {
"evidence": [
{
"text": "For reading lines from a file, you can loop over the file object.",
"url": "https://docs.python.org/3/tutorial/inputoutput.html",
"title": "7. Input and Output",
"blockIds": ["https://docs.python.org/3/tutorial/inputoutput.html#b062"],
"relevance": 0.97,
"answerLikelihood": 0.97,
"kind": "evidence"
}
],
"conflicts": [],
"pagesVisited": [ ... ],
"stopReason": "sufficient-evidence",
"usage": { ... }
},
"billing": {
"providerCost": 0.002701,
"billedCost": 0.020637,
"chargedUsd": 0.020637,
"balanceUsd": 4.979363
}
}
Request fields
| Field | Notes |
|---|---|
query | Required. The question to answer, in plain language. |
url | The page to start from. An open web search mode exists in the library but is not enabled on the hosted service yet, so an address is required. |
maxPages | Ceiling on pages processed. Default 25. |
maxDepth | How many link hops from the starting page. Default 3. |
maxCost | USD ceiling for this request. Default 0.05, with a hard maximum of 0.50. |
| Header | Notes |
|---|---|
Authorization | Bearer sk_live_.... Required. |
Content-Type | application/json. |
Idempotency-Key | Optional. Repeating a key returns the original run and does not charge a second time, so a retry after a timeout is safe. |
x-seekyo-model | Optional. Pin a specific model, if the deployment allows pinned models. |
Response fields
| Field | Notes |
|---|---|
result.evidence[] | The ranked passages. Each carries text, url, optional title, blockIds, relevance, answerLikelihood and kind. Ordered by how well the passage answers the question, not by how on-topic it is. |
result.conflicts[] | Passages that contradict the others, kept in their own list rather than merged, so a caller can report disagreement instead of averaging it away. |
result.pagesVisited[] | Every page fetched: url, finalUrl, parentUrl, depth, status, priority and reason. |
result.stopReason | Why the run ended: sufficient-evidence, frontier-exhausted, below-threshold, max-pages, max-depth, max-bytes, max-wall-clock, max-cost, max-semantic-decisions or aborted. |
result.usage | Counters for the run: fetches by kind, pages processed, semantic questions and tokens, and the cost breakdown. |
billing.billedCost | What the run is worth under the price book. |
billing.chargedUsd | What was actually taken from the balance. |
billing.balanceUsd | The balance after the charge. |
billing.estimate | The pre-flight estimate, alongside estimateSafetyFactor, the multiplier the gates used. |
billing.unbilledUsd | Present only if a run outran both its estimate and the account. |
The run's full internal trace is kept on the server for debugging and is not returned, so a response cannot leak the query strings or fragments of pages that were fetched and then rejected.
Errors
Every failure uses one envelope:
{
"error": {
"code": "insufficient-funds",
"message": "The account balance cannot cover the estimated cost of this request.",
"details": { "balanceUsd": 0.0012, "requiredUsd": 0.0224 }
}
}
| Status | Meaning |
|---|---|
| 400 | The request body is not valid JSON, or a field is the wrong type. |
| 401 | No key, a malformed header, an unknown key, or a revoked one. |
| 402 | The balance, or the request's cost ceiling, cannot cover the estimated work. |
| 404 | Unknown route. |
| 405 | Wrong method for that route. |
| 413 | The request body is too large. |
| 429 | Rate limited. The default is 60 requests per minute per key. |
| 502 | A provider or upstream fetch failed in a way the run could not continue through. |
Two things worth knowing before you debug
An origin whose robots.txt cannot be read is not crawled. If the file returns a server error, or the fetch fails, Seekyo treats that origin as disallowed and returns nothing from it. That is what RFC 9309 asks for, and it means a flaky robots.txt shows up as an empty result rather than a partial one.
The affordability check is conservative on purpose. Before any work
starts, the estimate is multiplied by a safety factor (2 by default) and compared against
maxCost and the balance. A request whose raw estimate fits can therefore
still be refused. The error carries both figures and the multiplier, and the estimate
reported to you is never inflated.
Prices
Each request is priced from what it did. These are the hosted service's current defaults, and a deployment can configure every one of them.
| Operation | Price |
|---|---|
| Page fetched | $0.0005 |
| Page processed | $0.0005 |
| Semantic input tokens | $0.24 per million |
| Platform fee | $0.0005 per request |
Questions asked are counted and reported in usage, but they are not billed.
What they cost is billed as tokens, and pricing both would charge the same work twice.
A documentation crawl that reads two or three pages and stops as soon as it has the answer comes to roughly two cents. Credit is issued in advance, and a request will not start unless the balance covers it.
Crawler
Every page is fetched by a single identifiable crawler that respects robots.txt, honours
noindex and nofollow, and honours a declared
Crawl-delay. What it sends, and how to block it, is on the
crawler page.