n4nAI

Semantic Kernel setup tutorial: API keys and config

Step-by-step Semantic Kernel setup with n4n.ai API keys, including config patterns, provider fallback, and runnable code for chat completion and streaming.

n4n Team3 min read613 words

Audio narration

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

Semantic Kernel has become the de facto orchestration layer for .NET and Python developers building LLM applications. But the documentation assumes you’re calling Azure OpenAI or OpenAI directly. When you route through a gateway like n4n.ai — one OpenAI-compatible endpoint addressing 240+ models with automatic fallback when a provider is rate-limited or degraded — the configuration surface changes. This tutorial walks through the exact setup: installing packages, wiring API keys, configuring the kernel, and verifying the connection with chat completion and streaming.

Prerequisites

  • .NET 8 SDK or Python 3.10+
  • An n4n.ai account with an API key (grab it from the dashboard)
  • A code editor and terminal

The examples below use the n4n.ai base URL https://api.n4n.ai/v1. If you’re self-hosting or using a different gateway, swap the base URL accordingly.

Install the packages

.NET

dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI

Python

pip install semantic-kernel openai

The OpenAI connector is what Semantic Kernel uses to talk to any OpenAI-compatible endpoint — including n4n.ai.

Configure the kernel

The key insight: you don’t need a special “n4n.ai connector.” You configure the standard OpenAI connector with the n4n.ai base URL and your API key. Semantic Kernel’s HttpClient pipeline handles the rest.

.NET — programmatic configuration

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;

var builder = Kernel.CreateBuilder();

// n4n.ai OpenAI-compatible endpoint
var apiKey = Environment.GetEnvironmentVariable("N4N_API_KEY") 
    ?? throw new InvalidOperationException("Set N4N_API_KEY env var");
var baseUrl = "https://api.n4n.ai/v1";

builder.AddOpenAIChatCompletion(
    modelId: "gpt-4o-mini",        // any model n4n.ai serves
    apiKey: apiKey,
    httpClient: new HttpClient { BaseAddress = new Uri(baseUrl) },
    serviceId: "n4n-chat"          // optional, useful for multiple services
);

var kernel = builder.Build();

.NET — configuration via appsettings.json

{
  "SemanticKernel": {
    "ChatCompletion": {
      "N4N": {
        "ModelId": "gpt-4o-mini",
        "ApiKey": "${N4N_API_KEY}",
        "Endpoint": "https://api.n4n.ai/v1",
        "ServiceId": "n4n-chat"
      }
    }
  }
}
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion("N4N"); // matches the config section name
var kernel = builder.Build();

Python — programmatic configuration

import os
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

api_key = os.environ["N4N_API_KEY"]
base_url = "https://api.n4n.ai/v1"

kernel = Kernel()
kernel.add_service(
    OpenAIChatCompletion(
        ai_model_id="gpt-4o-mini",
        api_key=api_key,
        base_url=base_url,
        service_id="n4n-chat"
    )
)
export N4N_API_KEY="sk-..."
export N4N_BASE_URL="https://api.n4n.ai/v1"
export N4N_MODEL="gpt-4o-mini"
import os
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

kernel = Kernel()
kernel.add_service(
    OpenAIChatCompletion(
        ai_model_id=os.environ["N4N_MODEL"],
        api_key=os.environ["N4N_API_KEY"],
        base_url=os.environ["N4N_BASE_URL"],
        service_id="n4n-chat"
    )
)

Verify the connection — chat completion

Run a minimal request to confirm the kernel talks to n4n.ai and returns a completion.

.NET

using Microsoft.SemanticKernel;

var result = await kernel.InvokePromptAsync("Reply with exactly: pong");
Console.WriteLine(result); // Expected: pong

Expected output:

pong

Python

result = await kernel.invoke_prompt("Reply with exactly: pong")
print(result)  # Expected: pong

Expected output:

pong

If you see pong, the kernel, connector, API key, and base URL are all wired correctly.

Streaming responses

Streaming is where you feel the latency difference. Semantic Kernel exposes InvokePromptStreamingAsync (.NET) and invoke_prompt_stream (Python).

.NET streaming

using Microsoft.SemanticKernel;

await foreach (var chunk in kernel.InvokePromptStreamingAsync("Count from 1 to 5, one per line")) {
    Console.Write(chunk);
}
Console.WriteLine();

Expected output (streamed):

1
2
3
4
5

Python streaming

async for chunk in kernel.invoke_prompt_stream("Count from 1 to 5, one per line"):
    print(chunk, end="", flush=True)
print()

Expected output (streamed):

1
2
3
4
5

Using multiple models with service IDs

One kernel can hold multiple chat completion services. This is useful when you route different tasks to different models — say, a cheap model for classification and a reasoning model for code generation.

.NET

builder.AddOpenAIChatCompletion(
    modelId: "gpt-4o-mini",
    apiKey: apiKey,
    httpClient: new HttpClient { BaseAddress = new Uri(baseUrl) },
    serviceId: "fast"
);

builder.AddOpenAIChatCompletion(
    modelId: "o1-preview",
    apiKey: apiKey,
    httpClient: new HttpClient { BaseAddress = new Uri(baseUrl) },
    serviceId: "reasoning"
);

var kernel = builder.Build();

// Explicitly select the service
var fastResult = await kernel.InvokePromptAsync(
    "Classify: 'refund request' -> category",
    new() { { "service_id", "fast" } }
);

var reasoningResult = await kernel.InvokePromptAsync(
    "Write a recursive Fibonacci in Rust with memoization",
    new() { { "service_id", "reasoning" } }
);

Python

kernel.add_service(OpenAIChatCompletion(
    ai_model_id="gpt-4o-mini",
    api_key=api_key,
    base_url=base_url,
    service_id="fast"
))
kernel.add_service(OpenAIChatCompletion(
    ai_model_id="o1-preview",
    api_key=api_key,
    base_url=base_url,
    service_id="reasoning"
))

fast_result = await kernel.invoke_prompt(
    "Classify: 'refund request' -> category",
    service_id="fast"
)
reasoning_result = await kernel.invoke_prompt(
    "Write a recursive Fibonacci in Rust with memoization",
    service_id="reasoning"
)

Handling provider fallback and rate limits

n4n.ai returns standard OpenAI-compatible error codes. When a provider is rate-limited or degraded, the gateway automatically fails over to another provider serving the same model. Your code sees a successful response — no retry logic required.

However, if all providers for a model are exhausted, you’ll get a 429 Too Many Requests with a Retry-After header. Semantic Kernel’s OpenAI connector respects this header and retries automatically (configurable via OpenAIClientOptions).

.NET — custom retry policy

using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.Net.Http;

var httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };

// Semantic Kernel uses the OpenAI .NET SDK under the hood;
// you can pass a configured OpenAIClientOptions for retry behavior
var options = new OpenAIClientOptions
{
    RetryPolicy = new ExponentialBackoffRetryPolicy(
        maxRetries: 3,
        baseDelay: TimeSpan.FromSeconds(2)
    )
};

builder.AddOpenAIChatCompletion(
    modelId: "gpt-4o-mini",
    apiKey: apiKey,
    httpClient: httpClient,
    serviceId: "n4n-chat",
    clientOptions: options
);

Python — retry configuration

from openai import OpenAI
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion

# The connector accepts an OpenAI client instance
client = OpenAI(
    api_key=api_key,
    base_url=base_url,
    max_retries=3,
    timeout=60.0
)

kernel.add_service(OpenAIChatCompletion(
    ai_model_id="gpt-4o-mini",
    async_client=client,  # or client for sync
    service_id="n4n-chat"
))

Per-token usage metering

n4n.ai returns usage fields in the standard OpenAI response format (usage.prompt_tokens, usage.completion_tokens, usage.total_tokens). Semantic Kernel surfaces this via the FunctionResult metadata.

.NET

var result = await kernel.InvokePromptAsync("Summarize this in 10 words: ...");
var usage = result.Metadata?["usage"] as Microsoft.SemanticKernel.ChatCompletion.ChatCompletionUsage;
if (usage != null) {
    Console.WriteLine($"Prompt: {usage.PromptTokens}, Completion: {usage.CompletionTokens}, Total: {usage.TotalTokens}");
}

Python

result = await kernel.invoke_prompt("Summarize this in 10 words: ...")
usage = result.metadata.get("usage")
if usage:
    print(f"Prompt: {usage.prompt_tokens}, Completion: {usage.completion_tokens}, Total: {usage.total_tokens}")

Expected output:

Prompt: 42, Completion: 18, Total: 60

Common pitfalls

Symptom Cause Fix
401 Unauthorized API key missing or invalid Verify N4N_API_KEY env var; check dashboard for key rotation
404 Not Found Wrong base URL or model ID Ensure base URL ends with /v1; model ID must exist in n4n.ai catalog
HttpRequestException / ConnectionError Network / DNS / proxy Test curl -H "Authorization: Bearer $N4N_API_KEY" https://api.n4n.ai/v1/models
Streaming hangs Middleware buffering response Disable response buffering in reverse proxies (nginx: proxy_buffering off;)
Model not found Using a model alias n4n.ai doesn’t recognize List available models via GET /v1/models or the dashboard

Quick test: list available models

Before coding, verify your key works and see what models are routable.

curl -s -H "Authorization: Bearer $N4N_API_KEY" \
  https://api.n4n.ai/v1/models | jq '.data[].id' | head -20

Expected output (sample):

"gpt-4o-mini"
"gpt-4o"
"o1-preview"
"o1-mini"
"claude-3-5-sonnet-20241022"
"gemini-1.5-pro"
"llama-3.1-70b-instruct"
...

Use any of these IDs as modelId / ai_model_id in the kernel configuration.

What’s next

You now have a Semantic Kernel instance wired to n4n.ai with:

  • Correct base URL and API key configuration
  • Verified chat completion and streaming
  • Multi-model routing via service IDs
  • Automatic provider fallback (handled by the gateway)
  • Usage metering for cost tracking

From here, plug in planners, vector stores, or agent frameworks — the kernel doesn’t care which provider serves the tokens. The gateway handles model diversity, fallbacks, and per-token metering so your orchestration code stays clean.

Tagssemantic-kerneln4n-aiapi-keyssetup

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 semantic kernel getting started with n4n.ai posts →