Add an HTTP API to Microsoft GraphRAG with FastAPI

Add an HTTP API to Microsoft GraphRAG with FastAPI

Kimi Lu

Abstract cover art of a faceted knowledge sphere connecting to an arch, representing a GraphRAG HTTP API.

Add an HTTP API to Microsoft GraphRAG with FastAPI

Microsoft GraphRAG ships a CLI and Python API, but not a production HTTP service. To expose it over HTTP, load one fixed GraphRAG project, keep its index tables in memory for queries, and run expensive indexing as a controlled background job. Do not accept an arbitrary filesystem path from each request.

This guide uses GraphRAG's current graphrag.api interface with FastAPI. It provides three endpoints:

  • POST /index starts an index build and returns immediately.

  • GET /index/status reports the current job state.

  • POST /query runs a global search against the latest loaded index.

1. Create and Initialize the Project

GraphRAG currently requires Python 3.10–3.12. Create a virtual environment and install the dependencies:

python -m venv .venv
source .venv/bin/activate
python -m pip install "graphrag>=3.1,<4" fastapi "uvicorn[standard]" pandas pyarrow

Create a GraphRAG workspace:

mkdir rag
graphrag init --root ./rag

Add your provider configuration to rag/settings.yaml and the required secret to rag/.env. Put source documents in rag/input/, then build the first index:

graphrag index --root ./rag

The official GraphRAG quickstart explains model configuration and warns that indexing can consume substantial LLM resources. Start with a small dataset.

2. Add the FastAPI Service

Create api.py beside the rag directory:

import asyncio
from contextlib import asynccontextmanager
from pathlib import Path

import graphrag.api as graphrag_api
import pandas as pd
from fastapi import FastAPI, HTTPException, status
from graphrag.config.load_config import load_config
from pydantic import BaseModel, Field


PROJECT_ROOT = Path("./rag").resolve()
OUTPUT_DIR = PROJECT_ROOT / "output"


def load_index_tables() -> dict[str, pd.DataFrame]:
    required = {
        "entities": OUTPUT_DIR / "entities.parquet",
        "communities": OUTPUT_DIR / "communities.parquet",
        "community_reports": OUTPUT_DIR / "community_reports.parquet",
    }
    missing = [str(path) for path in required.values() if not path.exists()]
    if missing:
        raise FileNotFoundError(f"Missing GraphRAG index files: {', '.join(missing)}")
    return {name: pd.read_parquet(path) for name, path in required.items()}


class QueryRequest(BaseModel):
    query: str = Field(min_length=1, max_length=4_000)


index_state = {
    "status": "idle",
    "error": None,
}


@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.config = load_config(PROJECT_ROOT)
    app.state.index_lock = asyncio.Lock()
    try:
        app.state.tables = load_index_tables()
    except FileNotFoundError:
        app.state.tables = None
    yield


app = FastAPI(title="GraphRAG API", lifespan=lifespan)


async def rebuild_index() -> None:
    async with app.state.index_lock:
        index_state.update(status="running", error=None)
        try:
            results = await graphrag_api.build_index(config=app.state.config)
            errors = [error for result in results for error in result.errors]
            if errors:
                raise RuntimeError("; ".join(str(error) for error in errors))
            app.state.tables = load_index_tables()
            index_state["status"] = "complete"
        except Exception as error:
            index_state.update(status="failed", error=str(error))


@app.post("/index", status_code=status.HTTP_202_ACCEPTED)
async def start_index() -> dict[str, str]:
    if index_state["status"] == "running":
        raise HTTPException(status_code=409, detail="Indexing is already running")
    index_state.update(status="running", error=None)
    asyncio.create_task(rebuild_index())
    return {"status": "running"}


@app.get("/index/status")
async def get_index_status() -> dict[str, str | None]:
    return index_state


@app.post("/query")
async def query_index(request: QueryRequest) -> dict[str, str]:
    tables = app.state.tables
    if tables is None:
        raise HTTPException(status_code=409, detail="Build an index before querying")

    response, _context = await graphrag_api.global_search(
        config=app.state.config,
        entities=tables["entities"],
        communities=tables["communities"],
        community_reports=tables["community_reports"],
        community_level=2,
        dynamic_community_selection=False,
        response_type="Multiple Paragraphs",
        query=request.query,
    )
    return {"response": str(response)}

This example fixes the GraphRAG project root in server configuration. That avoids a path-traversal bug in which a caller could ask the service to index or read an arbitrary server directory.

3. Run the API

Start Uvicorn from the directory containing api.py:

uvicorn api:app --host 127.0.0.1 --port 3000

Keep it bound to 127.0.0.1 during development. If another service or user must reach it, add an authenticated proxy instead of exposing the development server directly.

4. Start and Monitor Indexing

Start a rebuild:

curl -X POST http://127.0.0.1:3000/index

Check its state:

curl http://127.0.0.1:3000/index/status

A successful run reloads the Parquet tables so later queries use the new index. The process keeps the previous in-memory tables available while a rebuild runs.

5. Query GraphRAG

Send a JSON request rather than putting the question in a URL:

curl -X POST http://127.0.0.1:3000/query \
  -H "Content-Type: application/json" \
  -d '{"query":"What are the main themes in these documents?"}'

The example uses global search. GraphRAG also offers local, DRIFT, and basic query methods; choose the method that matches the question and load the tables required by that API.

Production Checklist

Before deploying this service:

  1. Add authentication and authorization to every endpoint.

  2. Move indexing to a durable job queue if the process may restart or you need more than one worker.

  3. Store job state outside process memory.

  4. Limit request size, query length, concurrency, and model spend.

  5. Separate read traffic from index writes and switch index versions atomically.

  6. Redact secrets and sensitive document text from logs.

  7. Pin and test the GraphRAG version; its configuration and APIs can change between releases.

An HTTP endpoint is only the service boundary. If AI clients need portable discovery and tool schemas, read MCP vs API before deciding whether to expose the same operation through MCP as well.

Sources

About the author

Kimi Lu

Home

Home

Home

Integrations

Integrations

Integrations

Vault

Vault

Vault

Audit

Audit

Audit

Arana Grande

Arana Grande

Arana Grande

Free

Free

Free

30-day audit summary

30-day audit summary

30-day audit summary

Daily action-call volume and the latest receipts from the Loadout audit trail.

Daily action-call volume and the latest receipts from the Loadout audit trail.

Daily action-call volume and the latest receipts from the Loadout audit trail.

View Audit

View Audit

View Audit

Loadout usage

Loadout usage

Loadout usage

617 action calls in the last 30 days

617 action calls in the last 30 days

617 action calls in the last 30 days

May 19 - Jun 17

May 19 - Jun 17

May 19 - Jun 17

10 active days

10 active days

10 active days

Less

Less

Less

More

More

More

Recent activity

Recent activity

Recent activity

Latest action-call receipts from connected agents

Latest action-call receipts from connected agents

Latest action-call receipts from connected agents

Apr 23, 09:23 AM

Apr 23, 09:23 AM

Apr 23, 09:23 AM

Shopify

Shopify

Shopify

Creates Or Updates An Asset For A Theme

Creates Or Updates An Asset For A Theme

Creates Or Updates An Asset For A Theme

Success

Success

Success

Apr 23, 09:21 AM

Apr 23, 09:21 AM

Apr 23, 09:21 AM

Shopify

Shopify

Shopify

Update Products Param Product Id

Update Products Param Product Id

Update Products Param Product Id

Success

Success

Success

Apr 23, 08:53 AM

Apr 23, 08:53 AM

Apr 23, 08:53 AM

Shopify

Shopify

Shopify

Update Products Param Product Id

Update Products Param Product Id

Update Products Param Product Id

Failed

Failed

Failed

Apr 22, 22:13 PM

Apr 22, 22:13 PM

Apr 22, 22:13 PM

Shopify

Shopify

Shopify

Create Product Image

Create Product Image

Create Product Image

Success

Success

Success

Apr 22, 22:12 PM

Apr 22, 22:12 PM

Apr 22, 22:12 PM

Shopify

Shopify

Shopify

Create Product Image

Create Product Image

Create Product Image

Success

Success

Success

Connected integration coverage

Connected integration coverage

Connected integration coverage

162

162

162

of 753 accessible connected

of 753 accessible connected

of 753 accessible connected

Callable actions

Callable actions

Callable actions

1,126

1,126

1,126

Vault credentials

Vault credentials

Vault credentials

8

8

8

Explore what's possible

Explore what's possible

Explore what's possible

See all Integrations

See all Integrations

See all Integrations

Google Ads

Google Ads

Google Ads

All available Goolge Ads tools via...

All available Goolge Ads tools via...

All available Goolge Ads tools via...

X (twitter)

X (twitter)

X (twitter)

All available X tools via...

All available X tools via...

All available X tools via...

Github

Github

Github

All available Github tools via...

All available Github tools via...

All available Github tools via...

Notion

Notion

Notion

All available Notion tools via...

All available Notion tools via...

All available Notion tools via...

Slack

Slack

Slack

All available Slack tools via...

All available Slack tools via...

All available Slack tools via...

Firecrawl

Firecrawl

Firecrawl

All available Firecrawl tools via...

All available Firecrawl tools via...

All available Firecrawl tools via...

753 integrations are available for loadouts.

753 integrations are available for loadouts.

753 integrations are available for loadouts.

The one tool

for every tool

your agent needs.

Give any AI agent real capabilities in seconds. Connect 27,000+ tools once, skip the setup headache, and let your agents execute.

Try Aident Loadout

Empower your Codex or OpenClaws to get real jobs done. Connect 27,000+ tools in one prompt, and let your agents deliver real results.

Try Aident Loadout

Empower your Codex or OpenClaws to get real jobs done. Connect 27,000+ tools in one prompt, and let your agents deliver real results.

Try Aident Loadout

Empower your Codex or OpenClaws to get real jobs done. Connect 27,000+ tools in one prompt, and let your agents deliver real results.