API endpoint https://northerninference.ca/v1 Use this as the base URL in every integration.

LangChain / LlamaIndex

Use Northern Inference from LangChain and LlamaIndex with exact sovereign routes, Canadian data residency and custody records for regulated and public-sector builds.

Northern Inference is a sovereign Canadian AI API for government, defence, health and regulated teams, including organizations under the Controlled Goods Program. Every route carries PIPEDA-aligned Canadian data residency, an exact path to the latest frontier and open-weight models, and a per-request chain-of-custody record.

Get going in 30 seconds

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="claude-sonnet-4-6-t4",
    api_key="ni_live_YOUR_KEY_HERE",
    base_url="https://northerninference.ca/v1",
)

resp = llm.invoke("What model are you?")
print(resp.content)

Replace claude-sonnet-4-6-t4 with any route ID from northerninference.ca/portal (Models page).


LangChain (Python)

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="claude-sonnet-4-6-t4",
    api_key="ni_live_YOUR_KEY_HERE",
    base_url="https://northerninference.ca/v1",
)

resp = llm.invoke("What model are you?")
print(resp.content)

LangChain (JS/TS)

import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "claude-sonnet-4-6-t4",
  apiKey: process.env.NI_API_KEY,
  configuration: {
    baseURL: "https://northerninference.ca/v1",
  },
});

const resp = await llm.invoke("What model are you?");
console.log(resp.content);

LlamaIndex

from llama_index.llms.openai import OpenAI

llm = OpenAI(
    model="claude-sonnet-4-6-t4",
    api_key="ni_live_YOUR_KEY_HERE",
    api_base="https://northerninference.ca/v1",
)

print(llm.complete("What model are you?"))

Capturing the custody chain in your app

Every NI response carries custody headers. LangChain doesn't surface these through invoke(), but you can subclass ChatOpenAI or use .ainvoke_with_config() with a callback handler to capture them:

import httpx
from langchain_openai import ChatOpenAI

class NICustodyCapturingChatOpenAI(ChatOpenAI):
    last_custody_path: str = ""
    last_request_id: str = ""
    last_jurisdiction: str = ""

    def _get_http_client(self):
        transport = httpx.HTTPTransport()
        def log_response(response: httpx.Response):
            self.last_custody_path = response.headers.get("x-ni-custody-path", "")
            self.last_request_id = response.headers.get("x-ni-request-id", "")
            self.last_jurisdiction = response.headers.get("x-ni-resolved-jurisdiction", "")
        return httpx.Client(event_hooks={"response": [log_response]})

Or, simpler: use GET /api/usage/custody/{request_id} after each call to pull the full chain. The request_id is available on any failure via the x-ni-request-id header in the 4xx/5xx response too.

To attribute spend to a case file, contract or WBS code, send your own opaque reference on every call and it is recorded, shown in the portal and bound into the signed custody record. ChatOpenAI takes it as a default header:

llm = ChatOpenAI(
    model="claude-haiku-4-5-20251001-t4",
    api_key=NI_API_KEY,
    base_url="https://northerninference.ca/v1",
    default_headers={"X-NI-Reference": reference_for(case_id)},
)

Keep the value opaque (a keyed HMAC of your identifier, never the identifier itself); the full rules are in the curl guide under "Tag a request with your own reference".


Spot a problem? Write to support at northerninference dot ca.

↑