n4nAI

OpenRouter to n4n: comparing credit balances and refunds

Step-by-step guide to reconcile OpenRouter to n4n credit balances, check refund eligibility, and verify per-token metering after migration.

n4n Team4 min read816 words

Audio narration

Coming soon — every post will get a voice note here.

When you shift inference traffic from one gateway to another, the boring accounting matters. Reconciling OpenRouter to n4n credit balances ensures you don’t leave paid capacity stranded or misread your new metering. This how-to gives exact steps to export balances, assess refund options, and verify token-level deduction on the destination.

Step 1: Pull your OpenRouter credit state via API

OpenRouter exposes a credits endpoint that returns your total purchased, used, and remaining balance. Use it instead of scraping the dashboard.

curl -H "Authorization: Bearer $OPENROUTER_KEY" \
  https://openrouter.ai/api/v1/credits

The response shape is stable:

{
  "data": {
    "total_credits": 50.0,
    "used_credits": 12.37,
    "remaining_credits": 37.63
  }
}

In Python, wrap it so you can log the snapshot to your migration ledger:

import os, requests, json

def openrouter_balance(key: str) -> dict:
    r = requests.get("https://openrouter.ai/api/v1/credits",
                     headers={"Authorization": f"Bearer {key}"})
    r.raise_for_status()
    return r.json()["data"]

if __name__ == "__main__":
    bal = openrouter_balance(os.environ["OPENROUTER_KEY"])
    print(json.dumps(bal, indent=2))

Verification: you get HTTP 200 and a remaining_credits float. Record this number with a timestamp. The OpenRouter to n4n credit balances comparison starts with this figure.

Step 2: Review OpenRouter refund policy and eligibility

OpenRouter’s terms treat credits as a prepaid utility. Used credits are not refundable. Unused credits may be refunded at their support team’s discretion, typically via a manual ticket rather than self-service.

Do not write code for this. Open a support conversation with your account email, state the remaining balance from Step 1, and ask for the refund path. If they decline, plan to drain the balance (Step 6).

Engineers often miss that promotional credits granted by OpenRouter are separate from purchased credits and are almost never refundable. Inspect the total_credits composition if you have historical records; the API does not split them, so you need your billing email trail.

Step 3: Map model prices to estimate n4n equivalent burn

You need to know whether your remaining OpenRouter credits would have lasted longer or shorter on the new gateway. Pull the model list with pricing:

import requests

models = requests.get("https://openrouter.ai/api/v1/models").json()["data"]
for m in models[:5]:
    print(m["id"], m["pricing"])

Pricing is per-token credit cost for prompt and completion. Multiply by your historical token mix to get a projected burn.

n4n.ai meters per token on every request and exposes the same OpenAI-style usage object, so the math transfers directly. When projecting OpenRouter to n4n credit balances equivalence, assume a 1:1 token count and compare the per-token rate from each provider’s pricing page. If n4n is cheaper on a model you use heavily, your leftover OpenRouter credits were effectively over-funded.

Step 4: Repoint your client to the n4n endpoint

Swap the base URL. The client code does not change otherwise because the API is OpenAI-compatible.

from openai import OpenAI
import os

client = OpenAI(
    base_url="https://api.n4n.ai/v1",
    api_key=os.environ["N4N_KEY"]
)

resp = client.chat.completions.create(
    model="anthropic/claude-3.5-sonnet",
    messages=[{"role": "user", "content": "health check"}]
)
print(resp.choices[0].message.content)

n4n.ai provides one OpenAI-compatible endpoint covering 240+ models with automatic fallback when a provider is rate-limited. It also honors client routing directives and forwards provider cache-control hints, so your existing extra_headers for cache TTLs keep working.

Verification: the call returns a valid completion and resp.usage is populated.

Step 5: Verify per-token deduction on n4n

Send a known-size payload and inspect the usage block. Use a fixed string to make the token count reproducible.

import tiktoken

text = "def add(a,b): return a+b\n" * 10
enc = tiktoken.get_encoding("cl100k_base")
prompt_tokens = len(enc.encode(text))

resp = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": text}]
)
usage = resp.usage
assert usage.prompt_tokens == prompt_tokens, (usage.prompt_tokens, prompt_tokens)
print("prompt", usage.prompt_tokens, "completion", usage.completion_tokens)

If the assertion holds, your client and the gateway agree on tokenization. This confirms the per-token metering that backs your new credit balance.

For a full reconciliation of OpenRouter to n4n credit balances, run this test on each model family you use. Differences in tokenizer implementation can shift counts by a few percent; note them in your ledger.

Step 6: Reconcile leftover OpenRouter credits and decide on refund or drain

You now have three numbers: OpenRouter remaining (Step 1), projected n4n burn rate (Step 3), and verified n4n metering (Step 5). Act on the leftover.

If OpenRouter approved a refund: wait for the bank confirmation, then remove the key from your secret store.

If refund denied: drain the balance on non-critical traffic. Route low-priority batch jobs to OpenRouter while new interactive traffic hits n4n.

# Example: send low-priority summarization to OpenRouter until balance hits zero
import os, requests

def or_call(prompt: str):
    return requests.post(
        "https://openrouter.ai/api/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['OPENROUTER_KEY']}"},
        json={"model": "mistralai/mistral-7b-instruct",
              "messages": [{"role": "user", "content": prompt}]}
    ).json()

while openrouter_balance(os.environ["OPENROUTER_KEY"])["remaining_credits"] > 0.5:
    or_call("Summarize this log: " + "error 500 " * 50)

Stop the loop when remaining_credits drops below your minimum billing increment. The final reconciliation of OpenRouter to n4n credit balances should show OpenRouter near zero and n4n accumulating real usage.

Step 7: Confirm clean cutover

Watch both dashboards for 24 hours after you flip traffic. On OpenRouter, the credits endpoint should show no new used_credits movement beyond the drain loop. On n4n, per-token usage should reflect your production request volume.

# Quick diff check
or_before = openrouter_balance(os.environ["OPENROUTER_KEY"])["remaining_credits"]
import time; time.sleep(86400)
or_after = openrouter_balance(os.environ["OPENROUTER_KEY"])["remaining_credits"]
print("OpenRouter drift:", or_before - or_after)

Expected drift is zero (or equal to the small drain you intentionally ran). Any unexpected positive number means a stray client still points at the old key.

Notes on cache-control and routing directives

Both gateways accept cache_control in messages, but n4n forwards those hints to the upstream provider without modification. If you relied on OpenRouter’s internal cache discount, verify the same prefix caching works on n4n by checking usage.prompt_tokens_details.cached_tokens if the model returns it.

resp = client.chat.completions.create(
    model="anthropic/claude-3.5-sonnet",
    messages=[
        {"role": "system", "content": "You are a terse bot."},
        {"role": "user", "content": "Repeat: hello",
         "extra_body": {"cache_control": {"type": "ephemeral"}}}
    ]
)
print(resp.usage.model_dump())

If cached_tokens appears, your cache hit rate carries over and your credit projection stays accurate.

Closing the ledger

Migrating inference providers is mostly a billing discipline. You extracted the OpenRouter balance, checked refund reality, projected the n4n burn, verified token metering, drained or refunded the old wallet, and confirmed zero drift. The OpenRouter to n4n credit balances handoff is complete when both systems show the expected numbers and no orphaned calls remain. Keep the snapshot JSON from Step 1 in your infra repo; it is the only record that proves the cutover was clean.

Tagscreditsrefundsopenrouter

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All migrating from openrouter to n4n posts →