All resources
AI November 11, 2026 2 hours

Set up and ship something tiny (Module 0)

Get the plumbing working before the learning starts: run the notebooks, make one model call with a spend cap on it, keep your key out of your code, and put a working function on a public URL.

Depth
🟢 Conversational
Time
2 h (read 30 min, watch 0 min, build 1.5 h)
Operator path
0.75 h, no code
Before this
Nothing
API spend
$0 in mock mode. With a real model, one or two short calls; your log shows the exact cost.
Last verified
November 11, 2026

After this module you can

  • Run the course notebooks in Colab or on your own machine
  • Make a model call through the kit, and read its tokens and cost in the log
  • Put two spend caps on your key: one at the provider, one in the kit
  • Keep a key out of your code, and say what to do if one leaks
  • Deploy a small function to a public URL and call it

Most people who stall in a course like this don’t stall on the ideas. They stall on the plumbing: a package that won’t install, a key that won’t work, a bill they didn’t expect, a function that runs on their laptop and nowhere else.

So before any machine learning, get the plumbing working once. By the end of this module you’ll have run the course notebooks, made a model call with two spend caps on it, and put a tiny function on a public URL. Every module after this one builds on those three things.

The idea

What you need, and what it costs

You need three things, and only the first is required to start.

  1. Somewhere to run Python. Google Colab in a browser, or Python 3.11 to 3.13 on your own machine. Either works for every module.
  2. The starter kit. It holds the data, the notebooks and lib/llm.py, the small client every module uses to call a model. Modules 1 to 4 are classic machine learning on your laptop and never call a model.
  3. A model provider account, when you want real model calls. Until you add a key, the kit runs in mock mode, which costs nothing and runs every notebook. You’ll want a real key by Module 5, when embeddings start to matter.

For the deploy at the end of this module you also need an account on a hosting platform. The worked example uses Vercel; check its current plan limits before you rely on them.

What it costs is your own number, and the kit is built so you can see it. Every call is logged with its tokens and cost. The worked provider in this course is OpenRouter, which puts many providers’ models behind one key. It runs on prepaid credits, so you top up first and spend down. Some of its models have free variants, with IDs ending in :free, that come with daily request caps. Prices change too often to print here; your log is the source of truth.

Mock mode

Every module in this course runs without a key. With no LLM_API_KEY set (or with LLM_PROVIDER=mock), lib/llm.py answers every call itself instead of sending it to a provider:

  • A chat request gets a canned reply that starts with [mock reply].
  • A request for structured output gets JSON that matches the schema you asked for, with placeholder text in every field.
  • A request that offers tools gets a call to the first tool, with arguments that fit its schema.
  • An embedding request gets a word-hash vector: texts that share words end up close together, and nothing about meaning is learned.

Every mock call is logged like a real one, with a cost of $0 and rough token counts.

What mock mode proves: your code runs end to end, the pieces connect, and the checks you wrote catch what they should. What it can’t tell you: how good a model’s answers are, how much they vary from run to run, how long real calls take, or what they cost. Wherever a module quotes a number produced in mock mode, it says so. When you add a key, rerun the same notebook and compare.

Colab or your own machine

Colab needs nothing installed. Each module page has an “Open the notebook in Colab” button, and the notebook’s first cell fetches the kit and its packages. The trade-off is that Colab sessions reset. Files you create there vanish when the session ends, unless you save them somewhere.

Your own machine takes about 15 minutes to set up: clone the kit, make a virtual environment, install the requirements, copy .env.example to .env. The kit’s README has the exact commands. After that, everything persists, and you can open the code in whatever editor you like.

If you’re unsure, start in Colab. You can move later without losing anything that matters, because the kit is the same in both places.

Your key, and two spend caps

Create a key at your provider, then put two caps on it before you make a single real call.

The provider cap. On OpenRouter you can give each key a credit limit in dollars, with an optional daily, weekly or monthly reset. Once the key hits it, requests fail until the window resets or you raise the limit. This is the cap that matters most, because it holds no matter what code is calling: yours, a bug in yours, or someone who got hold of the key.

The kit cap. Set LLM_DAILY_BUDGET_USD in .env (or in Colab’s Secrets panel). Before every call, lib/llm.py adds up today’s logged spend and refuses to call once it reaches the budget. It’s a seatbelt for honest mistakes, like a loop that runs a thousand times instead of ten. It only sees calls that go through it, which is exactly why the provider cap still matters.

Keep the key out of your code

A key is a password that spends money. Treat it that way.

Where your API key lives Three safe places hold the key: a .env file on your laptop that git ignores, the Secrets panel in Colab, and a sensitive environment variable on Vercel that only the deployed function reads. All three send the key to the model provider. The unsafe places, a notebook cell, a git commit, a screenshot or a chat message, sit in a red box marked never here. Laptop .env (git ignores it) Colab Secrets panel Vercel env var, marked sensitive Your function reads it at runtime Model provider checks key, applies cap Never here: a notebook cell · a git commit · a screenshot · a chat message
The key lives in exactly one place per environment, and every one of them is outside the code.

The kit’s .gitignore already lists .env, and the Module 0 notebook checks two things for you: that git ignores .env, and that your key appears in no file git tracks.

If a key leaks anyway, the order matters. Revoke it at the provider first, then create a new one, then clean up. Deleting the commit doesn’t help: the key is still in the history, and you can’t know who copied it while it was visible. Assume a leaked key is in use until you’ve killed it.

Use an AI coding assistant, and check its work

You’ll write a lot of code in this course, and an AI coding assistant will speed that up a lot. Use one. Ask it to write a cell, then ask it to explain the cell line by line. If the explanation doesn’t make sense to you, you don’t understand the code yet, and that’s where the bugs will be.

Four habits keep it honest:

  • Run it before you trust it. Code that looks right and code that runs are different things. The notebooks’ done-when checks exist for this.
  • Check anything time-sensitive against the source. Assistants are confident about model names, API parameters and setup steps that changed months ago. For those, the vendor’s current docs win.
  • Try it on a small slice first. Ten rows, one call. Then scale up.
  • Never paste your key into it. Paste the error message, not the .env file.

Ship one function

Most of what you’ll build in this course has to run somewhere other than your laptop eventually. So ship something tiny now, while the stakes are low.

The steps are the same on every platform:

  1. Write a function that reads its secrets from environment variables, never from the code.
  2. List its dependencies in a file the platform understands.
  3. Set the secrets on the host, in the platform’s own settings.
  4. Deploy, and get a public URL.
  5. Call the URL and check what comes back.
  6. Limit what a caller can do, because anyone who finds the URL can call it, and every call spends your money.

The kit’s deploy/app.py is that function. It asks a model one fixed question, caps the reply at 60 tokens and returns JSON. With no key it answers in mock mode, so you can run it on your machine before it ever touches the internet.

Worked example: Vercel

We checked these steps against Vercel’s docs on November 11, 2026. Hosting platforms change their setup often. If a step doesn’t match what you see, Vercel’s Python runtime docs win.

Vercel’s Python runtime looks for an app named app in a file such as app.py, and reads dependencies from requirements.txt. The kit’s deploy/ folder already has both, with Flask as the only dependency. The default Python version is 3.12.

npm i -g vercel              # the Vercel CLI; needs Node.js
vercel login
cd deploy
vercel                       # creates the project; a new project's first deploy is production
vercel env add LLM_API_KEY --environment production --sensitive
vercel env add LLM_MODEL --environment production
vercel --prod                # redeploy, so the new variables apply
curl https://<your-project>.vercel.app/

Two details trip people up.

Environment variables only apply to new deployments. The running deployment keeps the values it was built with, which is why there’s a second deploy after adding them.

Use the short production URL. Vercel’s Standard Protection, available on every plan, protects every deployment URL except production domains. Depending on how your project is set up, the long per-deployment URL the CLI prints may ask for a Vercel login. The short <your-project>.vercel.app address is the one to call.

If everything worked, curl prints JSON with a one-sentence reply, the model that wrote it, the tokens used and the cost.

Why it matters for revenue

Every system later in this course, from lead scoring to drafting outreach, has to run on a schedule or behind a URL, without your laptop open. The first time you do that shouldn’t be the first time a client is watching.

Cost visibility matters just as much. “What does this cost per lead?” is the first question a finance person asks about an AI system, and the answer has to come from a log, not a guess. You start keeping that log today.

Where people go wrong

  • A key in a notebook cell, pushed to GitHub. It’s the most common leak, and it happens to careful people. Keys go in .env or Secrets, and the notebook checks for you.
  • Only a code-side cap. A budget check in your code can’t stop a bug that skips it or a leaked key. Set the provider’s limit first.
  • Testing only on the deployed URL. When it fails there, you can’t tell whether the code, the settings or the platform is at fault. Run the function locally first; the notebook does exactly that.
  • Forgetting to redeploy. You change a variable, nothing changes, and you start debugging code that’s fine.
  • Letting the caller choose the prompt. A public endpoint that takes any prompt and any length is a free model for whoever finds it, on your bill.

Build this

You start with the kit and the notebook 00-set-up-and-ship-something-tiny. The function is already written in deploy/. Your job is the setup around it.

  1. Open the notebook (the Colab button at the top of this page, or jupyter notebook locally) and run every cell. In mock mode it works with no key.
  2. Create a key at your provider and give it a credit limit. Put it in .env as LLM_API_KEY, with LLM_MODEL set to a model ID from your provider’s list. In Colab, use the Secrets panel instead.
  3. Add LLM_DAILY_BUDGET_USD=1.00 (or any number you’re comfortable with) the same way, then set provider_cap_set = True in the notebook.
  4. Rerun from the top. The call cell should now show a real reply, and the log line should show real tokens and a cost.
  5. Run python deploy/test_app.py in the kit folder. All checks should pass without a network connection.
  6. Deploy with the Vercel steps above, setting LLM_API_KEY and LLM_MODEL on the host.
  7. Paste your production URL into DEPLOYED_URL and run the last two cells. Fix anything marked with a cross.

Done when

  • Your function answers with JSON at a public URL
  • Your key is in no file git tracks, and git ignores .env
  • Your key has a credit limit at the provider, and LLM_DAILY_BUDGET_USD is set in the kit
  • Your call's tokens and cost show up in logs/llm_calls.jsonl

Use your own data

Change the fixed question in deploy/app.py to one that matters to your business, like "In one sentence, what does a demand planner do?" for a company that sells to distributors. Keep it fixed and keep MAX_TOKENS low. This is a public URL, so the caller should never get to choose the prompt.

Stretch

Make the function answer only callers who know a shared secret. Add a second environment variable on the host, check a request header against it in index(), and return a 401 when it doesn't match. Then call it with and without the header.

Operator path: no code

About 0.75 h. For readers who will direct or buy this work rather than write it.

  1. Open the solution notebook in Colab and choose Runtime → Run all. No key needed.
  2. Find the cell marked OPERATOR. OPERATOR_VALUE is the question sent to the model. Change it to any short question of your own (keep the quotes). Then choose Run all again.
  3. Answer these from what you saw:
    • In the call cell, how many tokens went in and how many came out? What did the log line record for cost?
    • After you changed the question, how did the input token count change, and why?
    • The spend-cap cell describes two caps. Which one still protects you if the code has a bug?
    • If a vendor's tool runs on your API key, what would you ask about where the key is stored and what limits it?

Check yourself

  1. Where should your API key live on your laptop, in Colab, and on Vercel?
    Show answer

    On your laptop, in .env, which git ignores. In Colab, in the Secrets panel. On Vercel, as an environment variable on the project, marked sensitive. Never in a notebook cell, a commit, a screenshot or a chat.

  2. Your kit budget is $0.002 a day. Today's log shows three calls costing $0.0004, $0.0011 and $0.0006. What happens on the next call?
    Show answer

    It's refused. Today's logged spend is $0.0021, which is at or over $0.002, and lib/llm.py checks that before every call and raises BudgetExceeded instead of calling.

  3. You pushed a commit with your key in it, then pushed another commit that deletes it. Are you safe?
    Show answer

    No. The key is still in the history, and you can't know who copied it while it was visible. Revoke the key at the provider first, create a new one, and only then clean up the repo.

  4. The kit already refuses calls past your daily budget. Why also set a credit limit at the provider?
    Show answer

    Because the kit only counts calls that go through it and get logged. A bug, a script that skips lib/llm.py, a leaked key or a deployed function bypasses it. The provider's limit holds no matter which code is calling.

  5. You changed LLM_MODEL on Vercel, but the URL still answers with the old model. Why?
    Show answer

    Vercel applies environment variable changes only to new deployments. The running one keeps the values it was built with. Redeploy with vercel --prod.

Ask a vendor

If you're buying this instead of building it, ask these. Here's what a good answer sounds like.

  1. Where do the API keys for our system live, and who can see them?

    A good answer: In the host's secret settings or a secrets manager, never in the code or the repo, with a named short list of people who can read them and a plan for rotating them.

  2. What stops a bug or a loop from running up a large bill?

    A good answer: A hard spending limit at the model provider, plus a budget check in the code, both with actual numbers, such as "the key is capped at $50 a month" (an illustrative figure; yours depends on volume).

  3. Can we see what each call costs?

    A good answer: Yes. Every call is logged with its model, tokens, cost and time, and you can get that log, not just a monthly total.

  4. Who can call the endpoints you deploy, and what can a caller change?

    A good answer: Named callers only, checked on every request, and the caller can't change the prompt or the reply length on anything that spends money.

Go deeper

  • Quickstart, OpenRouter · docs · 5 min · Free
    Creating an account and a key, and the first request. Skip the framework sections.
  • Limits (credit caps and rate limits), OpenRouter · docs · 5 min · Free
    The credit limits section: what the per-key limit does and what happens when you hit it. Skip the rest.
  • Using the Python runtime with Vercel Functions, Vercel · docs · 5 min · Free
    Python entrypoints, versions and dependencies. That's all this module uses.
  • Deploy a Flask app on Vercel, Vercel · docs · 5 min · Free
    Exporting the Flask application and deploying it. It matches the steps on this page.
  • Environment variables, Vercel · docs · 10 min · Free
    Read the environments table and the troubleshooting table, especially the row about changes needing a redeploy.

Found an error? Open an issue.

Found an error? Open an issue. See what changed.

Course text is free to reuse under CC BY 4.0, with credit to Everyway. The starter kit's code is MIT.

Everyway builds these systems for clients. If you'd rather have one built, get in touch.