index4ai
Official Python SDK for index4ai: index documents into a project namespace and get prompt-ready retrieval context for RAG from hybrid search.
Installation
Requires Python 3.10+.
pip install index4ai
Authentication
index4ai uses a project API key. Create one in the index4ai console (https://console.index4.ai) and set it in your environment:
export INDEX4AI_API_KEY="..."
or pass api_key= to the client. The client checks the key's prefix when it
is created and raises InvalidApiKeyError if you pass a key that is not a
project key, so a misconfiguration fails locally instead of as a 401/403.
Usage
from index4ai import Index4AI
client = Index4AI() # reads INDEX4AI_API_KEY
# 0. List the project's typed attributes BEFORE declaring any.
# Attribute names (and their types) are permanent per project and capped at
# `max_names`, so reuse an existing name instead of minting a near-duplicate.
known = {a.name: a.type for a in client.list_attributes().attributes}
# 1. Ingest a document. It is indexed into your project namespace; there is no
# archive to download. Typed attributes enable query-time filtering:
# string/int/float/bool are inferred; datetime and encrypted_text must be
# declared in attribute_schema.
result = client.ingest(
"document.pdf",
attributes={"source": "treasury", "published_at": "2023-01-01T00:00:00Z"},
attribute_schema={"published_at": {"type": "datetime"}},
)
print(result.doc_id, result.status, result.usage)
# 1b. Or let the server fetch the file from a public URL — no upload.
# Optionally POST a webhook when the job finishes; the value is
# "<url>|<header>:<value>|..." (capped at 1024 chars).
result = client.ingest(
remote_url="https://example.com/document.pdf",
completion="https://example.com/hook|x-api-key:secret",
)
# 2. Hybrid search across all documents in the namespace. Returns a
# prompt-ready XML+Markdown block — drop it straight into an LLM prompt.
ctx = client.search(
"How do I configure retries?",
target_tokens=2048,
attribute_filters=[{"attr": "source", "op": "eq", "value": "treasury"}],
)
print(ctx.context)
# 3. List, inspect, and delete documents. list_docs() follows pagination and
# returns every document in the namespace.
for doc in client.list_docs():
print(doc.doc_id, doc.filename, doc.pages)
deleted = client.delete_doc(result.doc_id)
print(deleted.vectors_deleted, deleted.storage_deleted)
The document id
ingest() / collect() return an IngestResult whose doc_id is the id to
search and manage the document by. If the same content was already indexed,
the server keeps the existing document instead: deduplicated is True and
doc_id is the pre-existing document's id. doc_id is None only when the
server reported no indexing outcome.
Submitting and waiting separately
ingest() is submit() followed by collect(). To submit many files and
collect later, split them:
job_id = client.submit("document.pdf")
# ... later, even from another process:
result = client.collect(job_id)
Waiting can always be resumed with the same job_id. Submitting the file
again instead creates a second job. Two errors tell you which case you are in:
JobWaitError— the job exists and keeps running; only the wait failed (network, unreadable status). Resume withclient.collect(err.job_id).SubmitUncertainError— the connection failed mid-submit, so the server may or may not have created a job. Checklist_docs()before submitting again.
Listing attributes: list before you declare
list_attributes() returns an AttributeList: attributes (a list of
AttributeInfo(name, type)), max_names (the project's cap on distinct names)
and note (the server's guidance).
Attribute names are permanent per project — the first write of a name fixes
its type and spends one of the max_names slots, and clearing a value does not
free the name. Call list_attributes() before declaring attributes and reuse an
existing name with its existing type.
A 503 means the attribute schema is temporarily unreadable. It is raised as
ApiError with status_code == 503 — retry later; never treat it as an
empty list, or you will mint duplicate names. A response without an
attributes list raises InvalidResponseError for the same reason.
Async
AsyncIndex4AI mirrors the sync client method for method:
import asyncio
from index4ai import AsyncIndex4AI
async def main() -> None:
async with AsyncIndex4AI() as client:
ctx = await client.search_in_doc("summarize section 3", "doc_abc123")
print(ctx.context)
asyncio.run(main())
Migrating from poma.Grill
Before (poma ≤ 0.7) |
Now (index4ai) |
|---|---|
pip install poma |
pip install index4ai |
from poma import Grill, AsyncGrill |
from index4ai import Index4AI, AsyncIndex4AI |
POMA_GRILL_API_KEY |
INDEX4AI_API_KEY (same project key) |
GrillIngestResult (find the doc via list_docs()) |
IngestResult with doc_id |
GrillContext |
SearchContext |
InvalidGrillApiKeyError |
InvalidApiKeyError |
labels / meta_int_*, labels_any… / meta_int_*_gte… |
attributes / attribute_schema, attribute_filters |
return_page_images |
removed |
Configuration
| Environment variable | Purpose | Default |
|---|---|---|
INDEX4AI_API_KEY |
Project API key | — (required) |
INDEX4AI_API_BASE_URL |
API host override | https://api.index4.ai |
The client targets API version 1 (https://api.index4.ai/v1).
Metadata
Release files for index4ai 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| index4ai-0.1.0.tar.gz | 33.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| index4ai-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 57.0 kB
Release files / index4ai-0.1.0.tar.gz
| Download URL | index4ai-0.1.0.tar.gz |
|---|---|
| Size | 33.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e50ad43d36e87e8ff4a9b2d6e3fe2b9431f9e981951ea4d018e1b7f32d2f1d88
|
|
BLAKE2b-256 checksum How to use checksums |
4bd25ee99016fade2924dd2fdda8e2ff3a01e03fea95a918e79fdf776e7717b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / index4ai-0.1.0-py3-none-any.whl
| Download URL | index4ai-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a2309b2ee4fed97cc73b5a78956c546ab67ea4d72893ad857601e22c6e00e3d8
|
|
BLAKE2b-256 checksum How to use checksums |
61f40eb96564f676ec59eed23fd75be1da3b870f111ace202dcb2178495bdc40
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency log