NanoGPT API Tutorial: Python Guide with Code Examples

if you can use OpenAI's Python SDK, you can use NanoGPT. the API is identical. i switched my whole workflow over in about 10 minutes. here's everything you need to know, with working code you can copy-paste right now.

tl;dr: NanoGPT's API is identical to OpenAI's. same SDK, same request format, just change the base URL. this tutorial covers basic usage, streaming, function calling, error handling, and practical examples you can copy-paste right now.

Key Takeaways:

  • NanoGPT API uses the exact same OpenAI Python SDK. just change base_url to api.nano-gpt.com/v1
  • streaming, function calling, and model switching work identically to OpenAI with zero code changes
  • always set max_tokens for routine tasks to avoid paying for 4000 tokens when you only needed 200

👉 Get NanoGPT with 5% discount - you'll need an API key for this tutorial.


setup

install the OpenAI SDK

short answer: pip install openai. the official OpenAI SDK works directly with NanoGPT's API format.

pip install openai

that's the only dependency. NanoGPT uses the OpenAI API format, so the official SDK works directly.

basic configuration

short answer: set base_url to api.nano-gpt.com/v1 and use your NanoGPT API key. always use environment variables, never hardcode keys.

import openai

client = openai.OpenAI(
    base_url="https://api.nano-gpt.com/v1",
    api_key="your-nanogpt-api-key"
)

important: never hardcode your API key in source code. use environment variables:

import os
import openai

client = openai.OpenAI(
    base_url="https://api.nano-gpt.com/v1",
    api_key=os.environ.get("NANOGPT_API_KEY")
)

set the environment variable:

export NANOGPT_API_KEY="your-key-here"

see our API key setup guide for getting your key.


basic usage

simple chat completion

short answer: create a client with NanoGPT base URL, send messages with model name, print the response. identical to OpenAI.

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "Explain Python list comprehensions in 2 sentences."}
    ]
)

print(response.choices[0].message.content)

with system prompt

short answer: add a system message to set the model's behavior. works exactly like OpenAI's system prompts.

response = client.chat.completions.create(
    model="claude-3-5-sonnet",
    messages=[
        {"role": "system", "content": "You are a Python expert. Be concise."},
        {"role": "user", "content": "What's the difference between a list and a tuple?"}
    ]
)

print(response.choices[0].message.content)

multi-turn conversation

short answer: append assistant and user messages to maintain conversation history across multiple API calls.

messages = [
    {"role": "system", "content": "You are a helpful coding assistant."},
    {"role": "user", "content": "Write a function to reverse a string."},
]

# first response
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages
)
assistant_msg = response.choices[0].message.content
print(assistant_msg)

# follow-up
messages.append({"role": "assistant", "content": assistant_msg})
messages.append({"role": "user", "content": "Now make it handle None values."})

response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages
)
print(response.choices[0].message.content)

streaming

streaming gives you token-by-token output. essential for chat applications.

basic streaming

short answer: set stream=True and iterate over chunks. print content as it arrives for real-time response display.

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Write a short story about a robot."}],
    stream=True
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)

print()  # newline after streaming

streaming with collection

short answer: stream and collect the full response simultaneously. useful for displaying and saving the complete output.

def stream_response(client, model, messages):
    """Stream response and return full text."""
    stream = client.chat.completions.create(
        model=model,
        messages=messages,
        stream=True
    )
    
    full_response = ""
    for chunk in stream:
        content = chunk.choices[0].delta.content
        if content:
            full_response += content
            print(content, end="", flush=True)
    
    print()
    return full_response

# usage
response_text = stream_response(client, "gpt-4o", [
    {"role": "user", "content": "Explain async/await in Python."}
])

advanced features

function calling (tools)

short answer: define tools in the same format as OpenAI. the model calls functions and you send results back. works with GPT-4o, Claude, Gemini.

import json

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather for a city",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "City name"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
    tools=tools,
    tool_choice="auto"
)

# check if model wants to call a function
if response.choices[0].message.tool_calls:
    tool_call = response.choices[0].message.tool_calls[0]
    function_name = tool_call.function.name
    arguments = json.loads(tool_call.function.arguments)
    
    print(f"Model wants to call: {function_name}")
    print(f"Arguments: {arguments}")
    
    # simulate function result
    weather_result = {"temp": "22°C", "condition": "sunny"}
    
    # send result back to model
    messages = [
        {"role": "user", "content": "What's the weather in Tokyo?"},
        response.choices[0].message,
        {
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps(weather_result)
        }
    ]
    
    final_response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages
    )
    print(final_response.choices[0].message.content)

controlling output

short answer: set max_tokens to limit response length and save money. temperature controls creativity. top_p controls sampling.

control how the model responds:

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Write a haiku about Python."}],
    max_tokens=100,        # limit response length (saves money)
    temperature=0.9,       # higher = more creative (0-2)
    top_p=0.95,            # nucleus sampling
    frequency_penalty=0.1, # reduce repetition
    presence_penalty=0.1   # encourage new topics
)

print(response.choices[0].message.content)

tip: always set max_tokens for routine tasks. without it, the model might generate 4000 tokens when you only needed 200. that's wasted money.

model switching

short answer: switch models by changing one parameter. ask the same question to GPT-4o, Claude, and DeepSeek with one function.

def ask_any_model(model, question):
    """Ask any NanoGPT model a question."""
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": question}]
    )
    return response.choices[0].message.content

# same question, different models
question = "What is recursion?"

gpt4_answer = ask_any_model("gpt-4o", question)
claude_answer = ask_any_model("claude-3-5-sonnet", question)
deepseek_answer = ask_any_model("deepseek-v3", question)

print("GPT-4o:", gpt4_answer)
print("Claude:", claude_answer)
print("DeepSeek:", deepseek_answer)

error handling

common errors and fixes

short answer: 401: invalid key. 429: rate limited, add backoff. 500: server error, retry. timeout: increase timeout or try smaller model.

from openai import (
    APIError,
    RateLimitError,
    AuthenticationError,
    APITimeoutError
)

def safe_api_call(client, model, messages, max_retries=3):
    """API call with retry logic."""
    for attempt in range(max_retries):
        try:
            response = client.chat.completions.create(
                model=model,
                messages=messages,
                timeout=30  # 30 second timeout
            )
            return response.choices[0].message.content
            
        except AuthenticationError:
            print("Invalid API key. Check your NANOGPT_API_KEY.")
            return None
            
        except RateLimitError:
            wait_time = 2 ** attempt
            print(f"Rate limited. Waiting {wait_time}s...")
            time.sleep(wait_time)
            
        except APITimeoutError:
            print(f"Timeout on attempt {attempt + 1}/{max_retries}")
            if attempt == max_retries - 1:
                return None
                
        except APIError as e:
            print(f"API error: {e}")
            if attempt == max_retries - 1:
                return None
            time.sleep(1)
    
    return None

error reference

short answer: 401 Unauthorized: check key. 429 Rate Limited: add retry with backoff. 500: retry after delay. timeout: increase timeout.

common errors you'll encounter and how to fix them:

ErrorCauseFix
401 UnauthorizedInvalid API keyCheck key in dashboard
429 Rate LimitedToo many requestsAdd retry with backoff
500 Internal ErrorNanoGPT server issueRetry after delay
502/503Service unavailableWait and retry
TimeoutSlow responseIncrease timeout, try smaller model

practical examples

example 1: code reviewer

short answer: send code to Claude 3.5 Sonnet with a review system prompt. returns specific, actionable feedback.

def review_code(code, language="python"):
    """Get AI code review."""
    response = client.chat.completions.create(
        model="claude-3-5-sonnet",
        messages=[
            {"role": "system", "content": "You are a senior code reviewer. Be direct and specific."},
            {"role": "user", "content": f"Review this {language} code:\n\n```{language}\n{code}\n```"}
        ]
    )
    return response.choices[0].message.content

# usage
code_to_review = """
def add(a, b):
    return a + b
"""
print(review_code(code_to_review))

example 2: multi-model comparison

short answer: ask the same question to GPT-4o, Claude 3.5, and DeepSeek V3. compare responses side by side.

def compare_models(question, models=None):
    """Ask the same question to multiple models."""
    if models is None:
        models = ["gpt-4o", "claude-3-5-sonnet", "deepseek-v3"]
    
    results = {}
    for model in models:
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": question}],
            max_tokens=500
        )
        results[model] = response.choices[0].message.content
    
    return results

# usage
answers = compare_models("What's the best Python web framework?")
for model, answer in answers.items():
    print(f"\n--- {model} ---")
    print(answer[:200] + "...")

example 3: document summarizer

short answer: use Gemini 1.5 Pro for long document summarization with its massive context window.

def summarize_document(text, max_length=200):
    """Summarize a long document."""
    response = client.chat.completions.create(
        model="gemini-1.5-pro",  # good for long contexts
        messages=[
            {"role": "system", "content": f"Summarize in under {max_length} words."},
            {"role": "user", "content": text}
        ]
    )
    return response.choices[0].message.content

# usage
with open("long_document.txt", "r") as f:
    document = f.read()

summary = summarize_document(document)
print(summary)

example 4: conversation memory

short answer: ChatBot class maintains message history across turns. the model remembers previous context.

class ChatBot:
    """Simple chatbot with conversation memory."""
    
    def __init__(self, model="gpt-4o", system_prompt="You are a helpful assistant."):
        self.client = openai.OpenAI(
            base_url="https://api.nano-gpt.com/v1",
            api_key=os.environ.get("NANOGPT_API_KEY")
        )
        self.model = model
        self.messages = [{"role": "system", "content": system_prompt}]
    
    def chat(self, user_message):
        self.messages.append({"role": "user", "content": user_message})
        response = self.client.chat.completions.create(
            model=self.model,
            messages=self.messages
        )
        assistant_message = response.choices[0].message.content
        self.messages.append({"role": "assistant", "content": assistant_message})
        return assistant_message

# usage
bot = ChatBot(model="claude-3-5-sonnet", system_prompt="You are a Python tutor.")
print(bot.chat("What is a decorator?"))
print(bot.chat("Show me an example."))  # remembers the previous context

example 5: batch processing

short answer: process multiple tasks with rate limiting. add delays between requests to avoid 429 errors.

import time

def batch_process(tasks, model="gpt-4o-mini", delay=0.5):
    """Process multiple tasks with rate limiting."""
    results = []
    for i, task in enumerate(tasks):
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": task}]
        )
        results.append(response.choices[0].message.content)
        print(f"Processed {i+1}/{len(tasks)}")
        time.sleep(delay)  # rate limit protection
    return results

# usage
tasks = [
    "Summarize: Python is a programming language...",
    "Translate to Spanish: Hello, how are you?",
    "Classify sentiment: This product is amazing!",
]

results = batch_process(tasks)

checking available models

# list all available models
models = client.models.list()
for model in models.data:
    print(model.id)

see our full model list for details on each model.


next steps

👉 Get started with NanoGPT


Last updated: July 2026


Disclosure: This article contains affiliate links. If you sign up through our referral link, you get a 5% discount and we earn a small commission. This doesn't affect our reviews - we pay for all services ourselves.

Ready to swap crypto privately?

No KYC. No account. Instant swaps.

Swap Now