Source: http://www.poma-ai.com/docs/grill/getting-started/quickstart

# Grill Quickstart

From a blank Grill project to **prompt-ready context** in three steps — **ingest → wait → search** — plus a fourth showing how to drop the result into an LLM call.

You need a **project API key** (prefix `poma_prod_gr_…`) — create one in the [Console](/grill/getting-started/projects). The account-level `POMA_API_KEY` (`poma_acc_…`) **cannot** call `/grill/*`.

> Prefer raw HTTP? Jump to [Direct API usage](#direct-api-usage) below. Every endpoint and field is in the [API reference](/grill/reference/api).

## With the Python SDK

The whole loop is four lines. `ingest` submits the file **and blocks until it's indexed** (the "wait" step is automatic); `search` returns the prompt-ready block.

```python
# pip install poma
import os
os.environ["POMA_GRILL_API_KEY"] = "poma_prod_gr_…"

from poma import Grill

g = Grill()                                     # validates the key prefix locally
g.ingest("annual-report.pdf")                   # 1 + 2 — submit, then wait until done
ctx = g.search("How did operating margin change year over year?")   # 3 — search
print(ctx.context)                              # XML + Markdown, ready for a prompt
```

Async — every method is `await`-able on `AsyncGrill`:

```python
import asyncio
from poma import AsyncGrill

async def main() -> None:
    async with AsyncGrill() as g:
        await g.ingest("annual-report.pdf")
        ctx = await g.search("operating margin year over year")
        print(ctx.context)

asyncio.run(main())
```

Full surface: [`Grill` reference](/sdk/reference/grill), [`AsyncGrill` reference](/sdk/reference/async-grill), [Grill in the SDK](/sdk/concepts/grill).

## 4. Use the context with an LLM

`ctx.context` is a single string — feed it straight to any model:

```python
import openai

resp = openai.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "Answer using ONLY the context. Cite document ids."},
        {"role": "user", "content": ctx.context},
    ],
)
print(resp.choices[0].message.content)
```

That's the whole Grill loop: ingest → search → prompt. No vector DB to set up, no reranker to tune, no prompt assembly to hand-write.

## Direct API usage

The same three steps with raw `curl`, if you're not using Python. Full request/response shapes and every option are in the [API reference](/grill/reference/api).

```bash
export POMA="https://api.poma-ai.com/v3"
export GRILL="$POMA/grill"          # so the calls below read $GRILL/ingest, $GRILL/search
export GRILL_KEY="poma_prod_gr_…"   # the SDK reads POMA_GRILL_API_KEY by the same name
```

### 1. Ingest a document

Raw bytes (`application/octet-stream`); the filename rides in `Content-Disposition` — required, it's how the server infers the file type.

```bash
JOB=$(curl -sS -X POST "$GRILL/ingest" \
  -H "authorization: Bearer $GRILL_KEY" \
  -H "content-type: application/octet-stream" \
  -H 'content-disposition: attachment; filename="annual-report.pdf"' \
  --data-binary @annual-report.pdf)

JOB_ID=$(echo "$JOB" | jq -r .job_id)   # response is a PublicJob — grab its id
```

### 2. Wait for the job to finish

Standard POMA job lifecycle (`pending` → `processing` → `done`/`failed`). Stream it, or poll it:

::: code-group

```bash [SSE stream]
curl -N "https://api.poma-ai.com/status/v1/jobs/$JOB_ID" \
  -H "authorization: Bearer $GRILL_KEY"
```

```bash [polling]
while :; do
  STATUS=$(curl -sS "$POMA/jobs/$JOB_ID/status" \
    -H "authorization: Bearer $GRILL_KEY" | jq -r .status)
  echo "status: $STATUS"
  case "$STATUS" in
    done|failed) break ;;
    *) sleep 2 ;;
  esac
done
```

:::

At `done` the document is **already indexed** and searchable — no `.poma` archive to download.

### 3. Search

```bash
curl -sS -X POST "$GRILL/search" \
  -H "authorization: Bearer $GRILL_KEY" \
  -H "content-type: application/json" \
  -d '{
    "query": "How did operating margin change year over year?"
  }' | jq -r .context
```

You get back a `RetrievalContext` — a single string field of XML + Markdown, ready to paste into an LLM prompt:

```xml
<context>
<doc id="annual-report" title="Annual Report 2025" pages="42">
  [p42]
  ## Operating margin

  Operating margin rose from **18.4%** in FY24 to **21.1%** in FY25, …

  […]

  Cost-of-goods-sold improvements contributed roughly 1.6 pts …
</doc>
</context>
```

It's already sandwich-ordered, skip-marked (`[…]` / `[pN]`), token-budgeted, and citation-ready (`id` / `title` / `pages` on `<doc>`). See [RetrievalContext format](/grill/concepts/retrieval-context) for the grammar.

## Next

- [Ingestion](/grill/concepts/ingestion) — file types, labels & metadata, async semantics, redoing a doc.
- [Retrieval](/grill/concepts/retrieval) — `min_relevance`, `target_tokens` vs `max_tokens`, their defaults, and when to override each.
- [API reference](/grill/reference/api) — every endpoint and field.