← Back to list

Agentic AI Coding Basics 1— API Calls

Small read on how to make your first call, plus some cheap/free tips on where to start

D-A · 2026-06-28 19:16 · 0 claps · 6.9 min read
#llm-endpoints #openai-json-format #agentic-coding #ai-subscription #large-language-models
Open on Medium ↗
Wiki topics: LLM · Large Language Models AGT · AI Agents FT · Fine-tuning & Adaptation 💻 · Programming

Agentic AI Coding Basics 1— API Calls

What is the lowest level we can go as consumers of LLMs? — Inference API invocations; that’s it invoking what old people like me still call a WebService or endpoint is the lowest you can go.

We going to walk through the most basic use cases in here.

First, the world of inference APIs is split between 2 main formats defined by US companies, the obvious ones OpenAI format and the Anthropic format (there are others like DeepSeek’s DSML).

So far my experience has been that every single provider exposes all their models through BOTH kinds of endpoints (with the obvious exceptions being the 2 companies owning the main formats), thanks to that so far I’ve ONLY coded stuff using OAI-JSON format, nothing else.

For today we will stick to OAI, poking the free endpoints at OpenRouter.ai

So how does this work?

You pay or get a free API key to access a given endpoint, you POST your requests there and you get an answer; if you want to be more honest about it you can also mention SSE (Server-Sent Events) streaming support but that is getting into trickier spaces than what I would like for this delivery.

This SSE trick is how you get to see the thinking of the model on some tools, instead of a waiting screen and a sudden result, for me it is something crucial for proper behavioral monitoring (aka: did the model understood my shitty prompt?).

All endpoints require some kind of authentication, so people like OpenCode, OpenRouter, KiloCode, …. that offer to you a CLI/plugin with “free models” are just doing this for you transparently.

Some basics

It is plain JSON, depending on the model you can pass information in different formats (Multimodals can accept images/video/audio), but all JSON wrapped; the calls are made through basic HTTP protocol calls, so any programming language can be used or for these basic tests, or even curl for 1-shot scenarios.

Now the actual format , we going to do the very basic “Hello World” kind of thing.

The simple and most accesible way to do this is by using an OpenRouter free account and selecting any of his free models.

You can simply open an account with them copy/paste this and replace $OPENROUTER_API_KEY with YOUR key from the account just created and that’s it.

Your first manual API call!

  curl https://openrouter.ai/api/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $OPENROUTER_API_KEY" \
    -d '{
    "model": "openrouter/free",
    "messages": [
      {
        "role": "user",
        "content": "Say Hello World!"
      }
    ],
    "reasoning": {
      "enabled": true
    }
}'

Decomposing that a into small key pieces

“Content-Type: application/json”: If you need this explained, you first better read about fundamentals of the HTTP protocol.

“Authorization: Bearer”: Pretty much standard way of “sending your password” to the provider, some enterprise providers will get you SSO and the like but that is all that stays between your account/$$$ and someone else making use of it.

“model”: To which LLM on the backend do we want to talk to (GPT/Opus/GLM), in this case there is a “ Free Model Router” we will use that one, it will choose a model among the free ones on OpenRouter for us.

“messages”: This is where the content you send to the LLM goes, an array of messages; the role and content tell the LLM WHO and WHAT did he said. Some models are specially picky/tricky about this, so to do advanced stuff with them you need extra code/rules; to be more precise I’m talking of Minimax here.

“reasoning”: Do you want to see what the model “thought” in order to get you that conclusion/tool invocation sequence? — This is how you get it.

Before you see the call output remember we used a “Router” that actually will decide which model is used for the request, this is good for now but usually you’ll want to choose the specific model that fits the task, not just any model.

And yes, the ouput/response is a lot of relatively complex JSON, beautified here by jq:

curl https://openrouter.ai/api/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $OPENROUTER_API_KEY" \
    -d '{
    "model": "openrouter/free",
    "messages": [
      {
        "role": "user",
        "content": "Say Hello World!"
      }
    ],
    "reasoning": {
      "enabled": true
    }
}' | jq

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1391    0  1206  100   185    611     93  0:00:01  0:00:01 --:--:--   704
{
  "id": "gen-1782659899-WOe7TlLQalM7IzMFqaW9",
  "object": "chat.completion",
  "created": 1782659899,
  "model": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning-20260428:free",
  "provider": "Nvidia",
  "system_fingerprint": null,
  "service_tier": null,
  "choices": [
    {
      "index": 0,
      "logprobs": null,
      "finish_reason": "stop",
      "native_finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "Hello World!",
        "refusal": null,
        "reasoning": "We need to respond to \"Say Hello World!\" It's a simple request. Just output \"Hello World!\" Probably with exclamation. No extra.\n",
        "reasoning_details": [
          {
            "type": "reasoning.text",
            "text": "We need to respond to \"Say Hello World!\" It's a simple request. Just output \"Hello World!\" Probably with exclamation. No extra.\n",
            "format": "unknown",
            "index": 0
          }
        ]
      }
    }
  ],
  "usage": {
    "prompt_tokens": 20,
    "completion_tokens": 36,
    "total_tokens": 56,
    "cost": 0,
    "is_byok": false,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "cache_write_tokens": 0,
      "audio_tokens": 0,
      "video_tokens": 0
    },
    "cost_details": {
      "upstream_inference_cost": 0,
      "upstream_inference_prompt_cost": 0,
      "upstream_inference_completions_cost": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 33,
      "image_tokens": 0,
      "audio_tokens": 0
    }
  }
}

For our current example only a few sections matter.

The choices array, why an array? — Well you can generate more than 1 answer for a given input and here is where they would be returned.

You can see for example how OpenRouter choose/routed to “nvidia/nemotron-3-nano-omni-30b-a3b-reasoning-20260428:free, this was the actual model behind our reply, we could have used that name instead of “openrouter/free” and hit the exact same model.

"choices": [
    {
      "index": 0,
      "logprobs": null,
      "finish_reason": "stop",
      "native_finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "Hello World!",
        "refusal": null,
        "reasoning": "We need to respond to \"Say Hello World!\" It's a simple request. Just output \"Hello World!\" Probably with exclamation. No extra.\n",
        "reasoning_details": [
          {
            "type": "reasoning.text",
            "text": "We need to respond to \"Say Hello World!\" It's a simple request. Just output \"Hello World!\" Probably with exclamation. No extra.\n",
            "format": "unknown",
            "index": 0
          }
        ]
      }
    }
  ],

So which is the field we care the most? — content.

content” is the literal response of the LLM to whichever data you may have sent, in this case I just requested the model to say “Hello World!”.

Why all the other stuff, mostly control and signaling information for how the things went and who did the work (relevant here because we let OpenRouter choose the model for us).

One part you can opt-out in some models is the reasoning data, it isn’t like the model is not generating it, you just do not get that output back (but it is billed, so **not** a savings trick kind of toggle).

Output for the same request with reasoning set to false; note how the non-determinism hit us here and the model used also an emoticon to reply this time (completely unrelated to the reasoning toggle BTW).

{
  "id": "gen-1782667989-UicvEVl7NYkU4dVmfmSz",
  "object": "chat.completion",
  "created": 1782667989,
  "model": "nvidia/nemotron-3-ultra-550b-a55b-20260604:free",
  "provider": "Nvidia",
  "system_fingerprint": null,
  "service_tier": null,
  "choices": [
    {
      "index": 0,
      "logprobs": null,
      "finish_reason": "stop",
      "native_finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "Hello World! 👋",
        "refusal": null,
        "reasoning": null
      }
    }
  ],
  "usage": {
    "prompt_tokens": 20,
    "completion_tokens": 7,
    "total_tokens": 27,
    "cost": 0,
    "is_byok": false,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "cache_write_tokens": 0,
      "audio_tokens": 0,
      "video_tokens": 0
    },
    "cost_details": {
      "upstream_inference_cost": 0,
      "upstream_inference_prompt_cost": 0,
      "upstream_inference_completions_cost": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "image_tokens": 0,
      "audio_tokens": 0
    }
  }
}

So what’s the rest of that ouput?

Mostly provider defined fields, they are considered optional by most clients/CLIs/plugins.

— Technical information about the topic ends here, below section is mostly personal comments on transparency for costs/billing along personal choices on subscriptions. —

OpenRouter is impressively transparent and gives you a very authentic near real-time tracking or your expenses, they give you back all these crucial cost/token usage stats so you can monitor properly; which is surprisingly rare to find, believe or not all these super AI-Enabled corporations seem to do not know, or want to know, how to count tokens and give you a transparent bill in real time; but these dudes can. For real?

For example you get the bills for what you spend ~48hrs later (worst case scenario I know about), with no real-time tracking of your costs unless you write it yourself or use a third-third party in the middle; when using products/models that can cost you well $1-$5 per call a 1–2 day unmonitored bill can easily make a hole in anyones budget specially because a Dev is not going to make 1–2 or 10 calls it is going to invoke a process/workflow, the LLM is the one choosing how many and which calls to make in order to complete such task (ranging from find and fix X issue to decompose and implement this 5 stage implementation spec).

That is why I try to stay out of these monthly billed “PAYG” and choose to use either pre-paid balance (like OpenRouter) or flat rate plan based subscriptions like OpenCode Go, Minimax, Xiaomi Mimo, …

I do not use American models for personal projects because the cost of a single high-limits license is around $100–$200, and such high limits are usually 3–5X lower than the Chinese offers for usually way less money.

I’m already using the latest US models on my daytime job so no anxiety or FOMO on me; plus has been funny to see how freaking fast these models are growing in capacity, I really feel GLM5.2 coding performance is as good as Opus-4.7 which is SAYING A LOT. — Not fully there yet but is an impressive jump they have just made.

Notably DeepSeek has no token plan, but most Chinese models do have one, there are also companies hosting the models in the US (Ollama/Opencode/…).

Recommended Plans: Minimax, OpenCode, Ollama and Mimo.

Bad experiences: GLM, Model Ark (ByteDance), Alibaba Token Plan ($30 international one).

My main subs are Minimax and Ollama (another big name with 0 transparency on token counts/costs).

BTW seems I’m among the top consumers of the Minimax token plan, funny to see those 5.1 Billion tokens there — just picture that bill for a Sonnet or even worse an Opus model…

For me those were 2 months on the $10 sub (now gone) and less than a month with the $50 one, which has turned to be a real challenge to exhaust/spend fully.


메타데이터
post_id
70f88030b3cf
slug
agentic-ai-coding-basics-1-api-calls-70f88030b3cf
url
https://medium.com/@dastuam/agentic-ai-coding-basics-1-api-calls-70f88030b3cf
canonical_url
https://medium.com/@dastuam/agentic-ai-coding-basics-1-api-calls-70f88030b3cf
author_url
https://medium.com/@dastuam
status
ok
fetched_at
2026-07-14 12:48:22