Guides
- Quickstart
- Authentication and keys
- Key protection
- Models, aliases and fallbacks
- Automatic model choice (silicon/auto)
- Chat completions
- Streaming
- Structured outputs and tools
- Logprobs
- Vision
- Responses, Claude and Ollama
- Embeddings, rerank, audio, images
- Files and batches
- Documents and search
- LoRA adapters
- Project tags and costs
- Prefix caching
- Headers, rate limits and spend caps
- Errors
- Privacy and retention
- Verifiable receipts
- Data residency
- Pricing
- Billing
- Pay per request (x402)
- On-chain oracle
- Agent credit (backed compute lines)
- Status
- SDKs
- Swift SDK
- Integrations
- Run a node
- Architecture
- Changelog
API reference
Guide
Documents and search
Chat with your documents without running a database: upload files, put them in a hosted vector store, and search it, or let the model search it for you.
Vector stores are available where they are switched on for the deployment. Where they are off, every /v1/vector_stores route answers 404, uploads take purpose=batch only, and the file_search tool is refused with 400.
How it works
The API is OpenAI's Vector Stores API, so the official openai clients work unchanged with the base URL pointed at Silicon Network. Four steps:
- Upload each document with
POST /v1/filesandpurpose=assistants(oruser_data). - Create a vector store and attach the files. Each file is read, cut into overlapping chunks and embedded in the background.
- Poll the file (or the store's
file_counts) until it iscompleted. - Search the store with
POST /v1/vector_stores/{id}/search, or pass it to the Responses API as afile_searchtool and let the model answer from it.
Every chunk and every query is embedded with the platform's embedding model (bge-m3, multilingual) through the same serving path as /v1/embeddings, as your key. A store keeps the model it was created with, so its queries always match its chunks.
Quickstart
import time
from openai import OpenAI
client = OpenAI(base_url="https://api.siliconnetwork.io/v1", api_key="sn_live_...")
f = client.files.create(file=open("handbook.pdf", "rb"), purpose="assistants")
store = client.vector_stores.create(name="Handbook", file_ids=[f.id])
# Ingestion runs in the background.
while client.vector_stores.files.retrieve(f.id, vector_store_id=store.id).status == "in_progress":
time.sleep(2)
results = client.vector_stores.search(store.id, query="How many vacation days do I get?")
for r in results.data:
print(round(r.score, 3), r.filename, r.content[0].text[:80])
answer = client.responses.create(
model="llama-3.1-8b",
input="How many vacation days do I get?",
tools=[{"type": "file_search", "vector_store_ids": [store.id]}],
)
print(answer.output_text)curl https://api.siliconnetwork.io/v1/files -H "Authorization: Bearer $SILICON_API_KEY" \
-F purpose=assistants -F file=@handbook.md
curl https://api.siliconnetwork.io/v1/vector_stores -H "Authorization: Bearer $SILICON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Handbook", "file_ids": ["file-..."]}'
curl https://api.siliconnetwork.io/v1/vector_stores/vs_.../search -H "Authorization: Bearer $SILICON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "vacation days", "max_num_results": 5}'Files it can read
| Extension | How the text is read |
|---|---|
| .txt, .text, .md, .markdown | As is. UTF-8, or UTF-16 with a byte order mark. |
| .csv, .tsv | As is, one row per line. |
| .json, .jsonl | Parsed and printed compactly, one value after another. At most 64 levels of nesting. |
| .html, .htm | The visible text only: no scripts, styles or templates. |
| The text layer, page by page, of the first 2,000 pages. A scanned PDF without one has no text to index: there is no OCR. |
The type is decided by the extension, and the upload is checked before it is stored: text that is not UTF-8, JSON that does not parse or nests deeper than 64 levels, or a PDF that is not one is refused with 400. Documents are read in a separate process with CPU, memory and time limits, and one that needs more fails with invalid_file. A large JSON upload is checked the same way; when the gateway is busy reading other documents it answers 503 with Retry-After, so retry. A file is at most 32 MB and the organization's uploads share the Files API quota (see Files and batches). Uploaded files keep their own expiry, by default 30 days; a store keeps its own copy of the text, so search keeps working after the upload expires.
Stores and files
A store is {id, object: "vector_store", name, status, usage_bytes, file_counts, expires_after, expires_at, last_active_at, metadata}. Create it with POST /v1/vector_stores (name, file_ids, metadata, expires_after, chunking_strategy), list, read, modify and delete it as at OpenAI. Attach more files with POST /v1/vector_stores/{id}/files {file_id, attributes?, chunking_strategy?}.
File status
A file is in_progress until it settles as completed or failed; a failed file carries last_error with OpenAI's codes: unsupported_file, invalid_file (also when the organization's vector storage is full) or server_error. A temporary failure, such as no engine available for a moment or the attaching key at its rate limit, is retried automatically with a backoff, up to three attempts, before the file fails; a retry keeps the chunks already stored. Ingestion uses the attaching key's rate limit at batch priority, so it waits behind your interactive requests. A file that would not fit the organization's vector storage fails with invalid_file before any of it is embedded, and revoking the attaching key fails its files that are still in progress. The data residency of the attaching request (see Data residency) is kept with the file, so its chunks are embedded inside it.
Chunking
The default, or {"type": "auto"}, is chunks of 800 tokens that repeat the last 400 of the chunk before. Choose your own with {"type": "static", "static": {"max_chunk_size_tokens": 400, "chunk_overlap_tokens": 100}}: 100 to 4096 tokens, overlap at most half. Tokens are estimated (about four characters of English text a token, one per character for scripts such as Chinese or Japanese), so a chunk can be somewhat smaller than the model would count, never far larger.
Attributes
Attributes are up to 16 key and value pairs per file (strings up to 512 characters, numbers or booleans), set when you attach the file or later with POST /v1/vector_stores/{id}/files/{file_id}. Search filters read them.
Search
{
"query": "vacation days",
"max_num_results": 5,
"filters": {
"type": "and",
"filters": [
{"type": "eq", "key": "team", "value": "people"},
{"type": "gte", "key": "year", "value": 2025}
]
},
"ranking_options": {"score_threshold": 0.3}
}The answer is a page of results, one per chunk, best first: {file_id, filename, score, attributes, content: [{type: "text", text}]}. query may be a list of up to 16 strings; a chunk then scores by the query it matches best. max_num_results is 1 to 50 (default 10).
| Type | Matches when the file's attribute |
|---|---|
| eq, ne | equals, or does not equal, the value |
| gt, gte, lt, lte | is greater or smaller than the value (numbers, or strings in order) |
| in, nin | is, or is not, one of a list of values |
| and, or | all, or any, of the nested filters match |
A string never equals a number, and a file without the attribute matches only ne and nin. Scores are the cosine similarity of the query and the chunk; score_threshold drops results below it. Where a rerank model is configured for the deployment, search reranks its candidates with it and the score is the reranker's; send "ranker": "none" to skip that. A key whose model scope leaves out the rerank model gets cosine scores and the response header Silicon-Rerank: skipped-key-scope. rewrite_query is accepted and ignored: the query is searched as you sent it.
The file_search tool
Add {"type": "file_search", "vector_store_ids": ["vs_..."]} to the tools of a Responses request (optionally with max_num_results, filters and ranking_options). The gateway searches the stores with your latest user message before the model runs and gives the model the best excerpts, each with its file name, as one more user message after your input; the model never needs to call the tool. Your instructions are left as sent.
A non-streaming answer starts its output with a file_search_call item listing the query; add "include": ["file_search_call.results"] for the retrieved excerpts too. Streaming works the same for the answer, but the stream carries no file_search_call item, and neither does an answer with a signed receipt, whose body stays exactly as signed.
Excerpts are untrusted content. The gateway marks them as quoted file text, tells the model not to follow directions inside them, and stops a document from closing or faking the excerpt markup, but a model can still be swayed by what a document says. Only put documents in a store that you would paste into a prompt yourself, and do not let an answer built on them trigger actions without a check.
Billing and limits
| Item | Price |
|---|---|
| Embedding a file's chunks | The embedding model's per-token price, once per ingestion |
| Embedding a search query | The embedding model's per-token price |
| Reranking, where configured | The rerank model's per-token price |
| file_search excerpts | Input tokens of the Responses request, like any prompt |
| Storage | Free for now, and metered as usage_bytes |
The embedding calls appear in your usage like any /v1/embeddings request, under the key that attached the file or made the search (a search carries your project tag). See Pricing for the rates. A key whose scope leaves out the embedding model is refused with 403, and one without credit with 402, before anything is stored.
| Limit | Value |
|---|---|
| Vector storage per organization | 1 GB of usage_bytes (chunk text plus 4 bytes per vector dimension) |
| Stores per organization | 1,000 |
| Files per store | 10,000 |
| Chunks per file | 20,000 |
| File size | 32 MB, within the Files API quota |
| Chunks one search can scan, on a deployment without pgvector | 100,000 comparisons, counted once per query (a larger search answers 503: send fewer queries) |
Privacy and retention
A store keeps its chunks until you remove the file from it, delete the store, or the store expires. With "expires_after": {"anchor": "last_active_at", "days": 7} it expires seven days after it was last used (a search, an attached file or a change counts); an expired store answers with status expired and its chunks are deleted. Removing a file from a store leaves the upload in the Files API, as at OpenAI; delete it there too. A file uploaded with X-Silicon-No-Retention cannot be added to a store, because the store keeps its text. The attaching key is kept, encrypted, only while a file is being ingested, and deleting your account deletes all of your stores.
Not built yet: file batches (/file_batches), reading a store file's parsed content, query rewriting and OCR for scanned PDFs.