SDKs
Official Python, TypeScript and Go clients for the Redpine API.
Typed clients for the three languages most integrations are written in. All three are generated from the same OpenAPI spec as the API reference, so they expose the same endpoints, field names and errors as the REST API.
| Language | Package | Source |
|---|---|---|
| Python 3.10+ | redpine-sdk, import redpine | redpine-ai/redpine-sdk-python |
| TypeScript, Node 20+ | @redpine-ai/sdk | redpine-ai/redpine-sdk-js |
| Go 1.24+ | github.com/redpine-ai/redpine-sdk-go | redpine-ai/redpine-sdk-go |
Early release
The SDKs are at 0.x. The surface is stable for search, assisted search, collections and quota, but expect additive changes before 1.0. Report problems as issues on the language repository.
Install
pip install redpine-sdkYour first request
Create an API key from your API Keys page. Start with a sandbox key (sk_test_): it returns canned results at no cost, so you can get the request shape right without spending credits. Swapping it for a live key (sk_live_) later changes nothing else about your integration.
Put it in your environment. The clients read it from there; cURL passes it in the header.
export REDPINE_API_KEY=sk_test_YOUR_SANDBOX_KEYThen search a collection with a query. That is the whole first request. Don't know a collection name yet? GET /api/v1/search/collections lists every collection your key can reach, at no cost. See Collections.
curl -X POST "https://api.redpine.ai/api/v1/search/query" \
-H "Authorization: Bearer $REDPINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"collection": "my-collection", "query": "your search query"}'Results come back ranked by relevance. Each one carries the matched text, the collection it came from, and any metadata from the original document. Because this used a sandbox key the results are synthetic: note synthetic and the sandbox- id. A live key returns the same shape with real content.
{
"results": [
{
"id": "sandbox-0001-chunk-0",
"text": "Lorem ipsum dolor sit amet...",
"collection": "my-collection",
"metadata": { "title": "Lorem ipsum dolor", "synthetic": true }
}
],
"queryId": "qry_a1b2c3d4e5f6",
"latencyMs": 42
}Configure
Every client reads REDPINE_API_KEY from the environment. Pass the key explicitly if you would rather not use the environment. A sandbox key (sk_test_) works everywhere a live key does and costs nothing. See Authentication.
from redpine import Redpine
client = Redpine() # reads REDPINE_API_KEY
client = Redpine(api_key="sk_test_...", timeout=30.0, max_retries=2)Without a timeout of your own, the Python and Go clients wait 30 seconds for each call and 120 seconds for assisted search, which can take longer on a long query. A timeout you set applies to every call, assisted search included. The TypeScript client sets no timeout unless you pass timeoutMs.
Search
Pass exactly one of collection (a single collection) or collections (several at once). The response shape is the one documented under Search: results, queryId, latencyMs. Python exposes the same fields in snake case.
r = client.search("your search query", collection="my-collection", limit=10)
for hit in r.results:
print(hit.id, hit.text[:200])
print(r.query_id)Don't know a collection name? collections() lists every collection the key can reach, at no cost.
for c in client.collections().collections:
print(c.name)Filters
F builds the structured filter documented in Filtering without hand-writing JSON. Operators are eq, ne, in, not_in, gt, gte, lt, lte and between. Combine with and, or and not. A raw filter object is accepted anywhere a built filter is.
from redpine import F
recent_reviews = (
F("publication_date").gte("2022-01-01")
& F("article_type").eq("review")
& ~F("issn").in_(["1234-5679"])
)
r = client.search("crispr delivery", collections=["corpus"], filters=recent_reviews)Assisted search
Assisted search plans its own searches and verifies every result. Check status before reading results: it is results, clarification_needed or no_relevant_results.
a = client.assisted_search("what delivery vectors work for in vivo crispr?", collection="my-collection")
if a.status == "results":
for hit in a.results:
print(hit.relevance, hit.text[:200])
elif a.status == "clarification_needed":
print(a.clarification.question)Preview and unlock
Preview runs the search for free and returns teaser rows with a price per row; unlock pays for the rows you choose. Both return the same shape: results with locked, tokens and cost per row, plus costToUnlockRemaining and costCharged.
p = client.preview("your search query", collection="my-collection")
worth_it = [row.id for row in p.results if isinstance(row.tokens, int) and row.tokens < 300]
u = client.unlock(p.query_id, result_ids=worth_it) # result_ids=None unlocks every row
for row in u.results:
if not row.locked:
print(row.text)
print("charged", u.cost_charged)Re-sending an id you already paid for costs nothing, so retrying an unlock is safe.
Re-fetch results and check quota
Every search returns a queryId. Fetching it again within 7 days returns the same results without a new charge. Quota returns the daily and monthly counters described in Rate limits.
again = client.get_results(r.query_id)
q = client.quota()
print(q.daily_remaining, q.monthly_remaining)Errors and retries
Non-2xx responses raise one typed error per status, carrying status, code, message and requestId from the error envelope. The clients retry 429 and 503 automatically, honouring Retry-After and otherwise backing off exponentially with jitter, up to maxRetries (default 2). Nothing else is retried.
| HTTP | Python | TypeScript | Go |
|---|---|---|---|
| 401 | AuthError | AuthError | *AuthError |
| 402 | InsufficientCredits | InsufficientCredits | *InsufficientCreditsError |
| 403 | AccessDenied | AccessDenied | *AccessDeniedError |
| 404 | NotFound | NotFound | *NotFoundError |
| 410 | Expired | Expired | *ExpiredError |
| 422 | ValidationError | ValidationError | *ValidationError |
| 429 | QuotaExceeded (retry_after) | QuotaExceeded (retryAfter) | *QuotaExceededError (RetryAfter) |
| 503 | AssistedUnavailable | AssistedUnavailable | *AssistedUnavailableError |
| other | RedpineError | RedpineError | *APIError |
from redpine import QuotaExceeded, RedpineError
try:
r = client.search("...", collection="my-collection")
except QuotaExceeded as e:
print("retry after", e.retry_after)
except RedpineError as e:
print(e.status, e.code, e.message, e.request_id)Async Python
AsyncRedpine mirrors Redpine method for method on top of httpx. Use it as an async context manager or call aclose() when done.
from redpine import AsyncRedpine
async with AsyncRedpine() as client:
r = await client.search("your search query", collection="my-collection")Which one should I use?
Calling the API from a language not listed here, or from an environment where a dependency is unwelcome? The REST API in Get started needs nothing beyond an HTTP client. Connecting an AI agent rather than writing code? Use MCP.