Skip to main content

ConfamNode

The Nigerian AI inference gateway. Access frontier AI models.


Installation

Using pip

pip install confamnode

Using uv

uv add confamnode

Using virtualenv

# Create virtual environment
python -m venv venv

# Activate — Linux/Mac
source venv/bin/activate

# Activate — Windows
venv\Scripts\activate

# Install
pip install confamnode

Using uv with virtual environment

# Create project
uv init my-project
cd my-project

# Add confamnode
uv add confamnode

# Run your script
uv run python main.py

Using conda

# Create environment
conda create -n my-project python=3.10
conda activate my-project

# Install
pip install confamnode

Quick Start

from confamnode import ConfamNode

client = ConfamNode(api_key="confam-xxx")

ansa = client.gist(
    model="gemini/gemini-3.5-flash-lite",
    messages="How you dey?"
)

print(ansa.text)
print(f"Cost: ₦{ansa.cost.naira:.6f}")
print(f"Tokens: {ansa.usage.total_tokens}")
print(f"ID: {ansa.id}")

Streaming

from confamnode import ConfamNode

client = ConfamNode(api_key="confam-xxx")

stream = client.gist(
    model="gemini/gemini-3.5-flash-lite",
    messages="Wetin be the capital of 9ja?",
    stream=True
)

# Print tokens as they arrive
for yarn in stream:
    content = yarn.choices[0].delta.content
    if content:
        print(content, end="", flush=True)

# Get full Ansa after stream completes
ansa = stream.get_ansa()
print(f"\nModel: {ansa.model}")
print(f"Tokens: {ansa.usage.total_tokens}")
print(f"Cost: ₦{ansa.cost.naira:.6f}")
print(f"ID: {ansa.id}")

The Ansa Response Object

Every gist() call returns an Ansa object:

ansa = client.gist(model="gemini/gemini-3.5-flash-lite", messages="How you dey?")

# Response
ansa.text               # response text
ansa.model              # model that served the request
ansa.reasoning          # thinking trace (reasoning models only)
ansa.tools              # tool calls (agent models only)
ansa.citations          # citations (search models only)
ansa.finish_reason      # why generation stopped

# Usage
ansa.usage.prompt_tokens      # input tokens used (includes system message)
ansa.usage.completion_tokens  # output tokens used
ansa.usage.total_tokens       # total tokens used

# Cost — Naira first
ansa.cost.naira         # total cost in Naira ← primary
ansa.cost.naira_input   # input cost in Naira
ansa.cost.naira_output  # output cost in Naira

# Identity
ansa.is_local                # True — runs on Nigerian hardware
ansa.is_ngn_data_residency   # True — data never leaves Nigeria
ansa.id                      # unique request ID (confam-xxxx-xxxx)

# Response metadata
ansa.raw                     # dict with id, finish_reason, usage
ansa.raw["id"]                # provider response ID
ansa.raw["finish_reason"]    # why generation stopped
ansa.raw["usage"]            # prompt and completion token counts

Note: prompt_tokens includes any system message tokens. This is standard behaviour across all LLM providers (OpenAI, Anthropic, etc.).


Embeddings

Generate embeddings with client.embed(). The shape of what you get back mirrors the shape of what you send in: a single string returns one flat vector, a list returns a list of vectors.

from confamnode import ConfamNode

client = ConfamNode(api_key="confam-xxx")

# Single string in → one flat vector out
result = client.embed(
    model="liquid/lfm-2.5-embedding-350m:free",
    input="How you dey?"
)

print(result.embeddings)       # [0.123, -0.456, ...]
print(f"Cost: ₦{result.cost.naira:.6f}")
print(f"Tokens: {result.usage.total_tokens}")

# List in → list of vectors out, always — even with one item
result = client.embed(
    model="liquid/lfm-2.5-embedding-350m:free",
    input=["How you dey?", "Wetin dey happen?"]
)

print(len(result.embeddings))  # 2

Task types

Some embedding models expect a task-type hint (e.g. "query" vs. "document") to produce better embeddings for retrieval use cases. This defaults sensibly server-side if omitted — only pass it if you need a specific one:

result = client.embed(
    model="liquid/lfm-2.5-embedding-350m:free",
    input="find me a good jollof rice recipe",
    task_type="query"
)

print(result.task_type_applied)  # confirms what actually happened for this call

task_type_applied is None when the requested model doesn't use task-type hints at all — the field itself tells you whether it had any effect, no separate capability lookup needed.

The EmbeddingResult Object

result = client.embed(model="liquid/lfm-2.5-embedding-350m:free", input="How you dey?")

result.embeddings            # vector(s) — shape matches your input shape
result.model                 # model that served the request
result.usage.prompt_tokens   # input tokens used
result.usage.total_tokens    # total tokens used (embeddings have no output tokens to bill)
result.cost.naira            # total cost in Naira
result.cost.naira_input      # input cost in Naira
result.is_local              # True — runs on Nigerian hardware
result.is_ngn_data_residency # True — data never leaves Nigeria
result.task_type_applied     # which task-type hint was actually applied, if any
result.id                    # unique request ID
result.raw                   # full raw response

Models

Free Tier

Model Modality Input ₦/1M Output ₦/1M Input ₦/1K Output ₦/1K
gemini/gemini-3.1-flash-lite Text/Image/Video/File/Audio -> Text ₦0 ₦0 ₦0 ₦0
gemini/gemini-3.5-flash-lite Text/Image/Video/File/Audio -> Text ₦0 ₦0 ₦0 ₦0
nvidia/nemotron-3-super-120b-a12b:free Text -> Text ₦0 ₦0 ₦0 ₦0
nvidia/nemotron-3-ultra-550b-a55b:free Text -> Text ₦0 ₦0 ₦0 ₦0

Paid Tier

Model Modality Input ₦/1M Output ₦/1M Input ₦/1K Output ₦/1K
deepseek/deepseek-v4.1-flash Text/Image -> Text ₦704 ₦2,813 ₦0.704 ₦2.813

Embeddings

Free Tier

Model Modality Input ₦/1M Output ₦/1M Input ₦/1K Output ₦/1K
liquid/lfm-2.5-embedding-350m:free Text -> Embeddings ₦0 ₦0 ₦0 ₦0

Paid Tier

Model Modality Input ₦/1M Output ₦/1M Input ₦/1K Output ₦/1K
google/gemini-embedding-001 Text -> Embeddings ₦352 ₦0 ₦0.352 ₦0
google/gemini-embedding-2 Text/Image/File/Audio/Video -> Text ₦469 ₦0 ₦0.469 ₦0

More embedding models coming soon.

More models coming soon. Contact hello@confamnode.com for early access.


Pricing

All prices are in Nigerian Naira (₦). No USD. No conversion needed.

Tier How it works
Free Use immediately. No wallet needed. Shared capacity.
Paid Contact us to get access. Pay in Naira. No subscription. No expiry.

Currently in private beta. To get API access: hello@confamnode.com


Rate Limits

Rate limits vary by plan. Contact hello@confamnode.com.

  • Free tier — shared capacity with lower limits
  • Paid tier — dedicated higher limits

System Message

ConfamNode adds a default system message to every request giving the model a Nigerian identity and context. This is standard behaviour across all LLM providers and is counted in prompt_tokens.

# Use ConfamNode default identity (default)
ansa = client.gist(
    model="gemini/gemini-3.5-flash-lite",
    messages="Who are you?"
)
# "I am ConfamNode, Nigeria's AI inference gateway..."

# Override with your own system message
ansa = client.gist(
    model="gemini/gemini-3.5-flash-lite",
    messages="Who are you?",
    system="You are a helpful customer service agent for Konga."
)

# Disable system message entirely
ansa = client.gist(
    model="gemini/gemini-3.5-flash-lite",
    messages="Who you be?",
    system=None
)

Caching

Caching is controlled per request and is off by default — every call returns a fresh response, even when the request is identical. This keeps data-generation loops and any workflow that resends the same prompt from getting the same cached answer back each time.

Pass cache=True to let the gateway serve and store a cached response — useful for idempotent lookups:

# Default — caching off, fresh response every call
ansa = client.gist(
    model="deepseek/deepseek-v4.1-flash",
    messages="How you dey?"
)

# Enable caching — the gateway may return a stored response for a
# matching request, and store this one for next time
ansa = client.gist(
    model="deepseek/deepseek-v4.1-flash",
    messages="How you dey?",
    cache=True
)

Caching must be enabled for your account for cache=True to take effect. A cache hit returns near-instantly — the quickest way to see it is to send the same request twice with cache=True: the first call populates the cache, the second is served from it.

Note: a cache hit still bills the full Naira cost of the request — you're paying for inference value, not raw compute. Caching saves time, not money.


Reasoning Models

  • nvidia/nemotron-3-super-120b-a12b:free
  • nvidia/nemotron-3-ultra-550b-a55b:free
  • gemini/gemini-3.1-flash-lite
  • gemini/gemini-3.5-flash-lite
  • deepseek/deepseek-v4.1-flash

Enable extended thinking for complex problems:

ansa = client.gist(
    model="nvidia/nemotron-3-ultra-550b-a55b:free",
    messages="One trader buy goods for ₦50,000 sell am for ₦75,000. After e pay ₦5,000 for transport and ₦3,000 for market, wetin be the real profit? Show how you calculate am.",
    reasoning_effort="low"
    # one of: "high", "medium", "low", "minimal", "none"
)

print(ansa.reasoning)   # thinking trace
print(ansa.text)        # final answer

Also available on reasoning models for more complex multi-step problems:

ansa = client.gist(
    model="nvidia/nemotron-3-ultra-550b-a55b:free",
    messages="Analyse the financial risk of a Nigerian fintech expanding to Ghana...",
    reasoning_effort="high"
)

print(ansa.reasoning)   # full thinking trace
print(ansa.text)        # final analysis

Working with Images

def encode_image_b64(image_path: Path) -> tuple[str, str]:
    """Returns (base64_data, mime_type)."""
    mime = "image/jpeg"
    suffix = image_path.suffix.lower()
    if suffix == ".png":
        mime = "image/png"
    elif suffix == ".webp":
        mime = "image/webp"
    elif suffix in (".jpg", ".jpeg"):
        mime = "image/jpeg"
    data = base64.b64encode(image_path.read_bytes()).decode("utf-8")
    return data, mime

image_path = Path("<image_path>")
img_b64, mime = encode_image_b64(image_path)

messages = [
    {
        "role": "user",
        "content": [
            {"type": "text", "text": "Describe this image"},
            {
                "type": "image_url",
                "image_url": {"url": f"data:{mime};base64,{img_b64}"}
            }
        ],
    },
]

ansa = client.gist(
    model="gemini/gemini-3.5-flash-lite",
    messages=messages
)

if ansa.reasoning:
    print(f"***Reasoning Start***\n{ansa.reasoning}\n***Reasoning End***")
print(ansa.text)

Environment Variable

export CONFAMNODE_API_KEY="confam-xxx"
# No need to pass api_key explicitly
client = ConfamNode()

Custom Base URL

For enterprise clients running ConfamNode on private infrastructure:

client = ConfamNode(
    api_key="confam-xxx",
    base_url="http://your-private-server:4000/v1"
)

Error Handling

from confamnode import (
    ConfamAuthError,
    ConfamRateLimitError,
    ConfamModelError,
    ConfamInsufficientBalanceError,
    ConfamProviderError,
    ConfamRequestError,
    ConfamNodeError
)

try:
    ansa = client.gist(
        model="gemini/gemini-3.5-flash-lite",
        messages="How you dey?"
    )
except ConfamAuthError:
    print("Check your API key")
except ConfamInsufficientBalanceError:
    print("Wallet balance too low. Top up to continue.")
except ConfamRateLimitError:
    print("You don reach your limit. Contact hello@confamnode.com")
except ConfamModelError:
    print("Invalid model name")
except ConfamProviderError:
    print("The AI provider is temporarily unavailable. Try again shortly.")
except ConfamRequestError:
    print("Request could not be processed — check your parameters")
except ConfamNodeError as e:
    print(f"Something went wrong: {e}")

All specific exceptions above are subclasses of ConfamNodeError — catching that alone is enough if you don't need to distinguish error types.


REST API

ConfamNode provides an OpenAI-compatible REST API that you can call from any programming language or HTTP client without installing the ConfamNode SDK.

Base URL: https://api.confamnode.com/v1

Authentication

Set your API key as an environment variable:

export CONFAMNODE_API_KEY="confam-xxx"

All requests require Bearer-token authentication and a JSON content type.


1. Chat Completions

Endpoint: POST https://api.confamnode.com/v1/chat/completions

cURL

curl -X POST https://api.confamnode.com/v1/chat/completions \
  -H "Authorization: Bearer $CONFAMNODE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini/gemini-3.5-flash-lite",
    "messages": [
      {
        "role": "user",
        "content": "How you dey?"
      }
    ]
  }'

Python — HTTPX (Synchronous)

Install HTTPX:

pip install httpx
import os
import httpx

response = httpx.post(
    "https://api.confamnode.com/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {os.environ['CONFAMNODE_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "gemini/gemini-3.5-flash-lite",
        "messages": [
            {"role": "user", "content": "How you dey?"}
        ],
    },
    timeout=60.0,
)

response.raise_for_status()
ansa = response.json()

print(ansa["choices"][0]["message"]["content"])

Python — HTTPX (Asynchronous)

Use httpx.AsyncClient when working with async applications, such as FastAPI services or asynchronous workers.

import os
import asyncio
import httpx

async def main():
    async with httpx.AsyncClient(timeout=60.0) as client:
        response = await client.post(
            "https://api.confamnode.com/v1/chat/completions",
            headers={
                "Authorization": f"Bearer {os.environ['CONFAMNODE_API_KEY']}",
                "Content-Type": "application/json",
            },
            json={
                "model": "gemini/gemini-3.5-flash-lite",
                "messages": [
                    {"role": "user", "content": "How you dey?"}
                ],
            },
        )

        response.raise_for_status()
        ansa = response.json()

        print(ansa["choices"][0]["message"]["content"])

asyncio.run(main())

Note: In an existing async application, use await main() or await the request from your existing async function instead of calling asyncio.run().

JavaScript — Fetch API

Works in modern browsers and Node.js environments that support fetch. This example uses Node.js environment variables.

const response = await fetch(
  "https://api.confamnode.com/v1/chat/completions",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CONFAMNODE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "gemini/gemini-3.5-flash-lite",
      messages: [
        { role: "user", content: "How you dey?" },
      ],
    }),
  }
);

if (!response.ok) {
  throw new Error(
    `HTTP ${response.status}: ${await response.text()}`
  );
}

const ansa = await response.json();
console.log(ansa.choices[0].message.content);

2. Embeddings

Generate vector embeddings for semantic search, retrieval-augmented generation (RAG), and other machine-learning applications.

Endpoint: POST https://api.confamnode.com/v1/embeddings

cURL

curl -X POST https://api.confamnode.com/v1/embeddings \
  -H "Authorization: Bearer $CONFAMNODE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "liquid/lfm-2.5-embedding-350m:free",
    "input": "How you dey?"
  }'

Python — HTTPX (Synchronous)

import os
import httpx

response = httpx.post(
    "https://api.confamnode.com/v1/embeddings",
    headers={
        "Authorization": f"Bearer {os.environ['CONFAMNODE_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "liquid/lfm-2.5-embedding-350m:free",
        "input": "How you dey?",
    },
    timeout=60.0,
)

response.raise_for_status()
result = response.json()

print(result)

Python — HTTPX (Asynchronous)

import os
import asyncio
import httpx

async def main():
    async with httpx.AsyncClient(timeout=60.0) as client:
        response = await client.post(
            "https://api.confamnode.com/v1/embeddings",
            headers={
                "Authorization": f"Bearer {os.environ['CONFAMNODE_API_KEY']}",
                "Content-Type": "application/json",
            },
            json={
                "model": "liquid/lfm-2.5-embedding-350m:free",
                "input": "How you dey?",
            },
        )

        response.raise_for_status()
        result = response.json()

        print(result)

asyncio.run(main())

JavaScript — Fetch API

const response = await fetch(
  "https://api.confamnode.com/v1/embeddings",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CONFAMNODE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "liquid/lfm-2.5-embedding-350m:free",
      input: "How you dey?",
    }),
  }
);

if (!response.ok) {
  throw new Error(
    `HTTP ${response.status}: ${await response.text()}`
  );
}

const result = await response.json();
console.log(result);

Notes

  • Authentication: Include your API key in the Authorization: Bearer header for every request.
  • Content type: Send JSON requests using Content-Type: application/json.
  • Synchronous vs. asynchronous: Use httpx.post() for synchronous code and httpx.AsyncClient with await for asynchronous code.
  • API key security: Never expose your API key in public repositories or browser-side code. For frontend applications, make authenticated requests through your backend.
  • No SDK required: These examples call the REST API directly.
  • Pricing: Applicable API charges are calculated in Nigerian Naira (₦).

Private AI Deployment

Need data-residential private AI on your own infrastructure?

JoTeq the First offers:

  • On-premise deployment on Jetson devices and GPUs
  • RTX 3090/4090 bare metal setup
  • RAG pipelines and fine-tuning
  • Dedicated hosted models
  • SSH remote deployment

Contact: hello@confamnode.com



License

Apache 2.0


Metadata

Release files for confamnode 0.3.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for confamnode 0.3.3
File Size Uploaded Provenance
confamnode-0.3.3.tar.gz 37.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for confamnode 0.3.3
File Interpreter ABI Platform
confamnode-0.3.3-py3-none-any.whl Python 3 none any Details

Total release size: 56.8 kB

Release files / confamnode-0.3.3.tar.gz

Download URL confamnode-0.3.3.tar.gz
Size 37.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4aff8421acde1a848bb27c12a8fce08f70a274eae06b605d48ee58838c7818dd
BLAKE2b-256 checksum
How to use checksums
5a24440a3f93d90a701c754e0e8d9ba6fcf1391ee66882b14d47eee4611fc452
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / confamnode-0.3.3-py3-none-any.whl

Download URL confamnode-0.3.3-py3-none-any.whl
Size 19.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
145be41de576fa06d54e418db130442926a00a58c9d5b4bf61732e45ca0ecfcc
BLAKE2b-256 checksum
How to use checksums
5a95b94dc6b0f430850ea0517094f8aebc7c2e75aa1c6b88c34f8c8c3fe5df9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for confamnode 0.3.3

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page