{"id":223,"date":"2026-10-19T14:00:41","date_gmt":"2026-10-19T14:00:41","guid":{"rendered":"https:\/\/devdojo.co.in\/?p=223"},"modified":"2026-10-19T14:00:42","modified_gmt":"2026-10-19T14:00:42","slug":"genai-series-part-3-first-llm-api-calls","status":"publish","type":"post","link":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/","title":{"rendered":"Generative AI Part 3: Your First LLM API Calls in Python and Node.js"},"content":{"rendered":"\n<div class=\"dd-post\">\n<style>\n.single article:has(.dd-hero) .entry-media{display:none}\n.dd-post{--navy:#0b1020;--blue:#3178c6;--violet:#7c5cff;--ink:#1f2433;--muted:#5b6275;--line:#e3e6ef;font-size:17px;line-height:1.75;color:var(--ink)}\n.dd-post h2{margin:2.2em 0 .6em;font-size:1.6em;line-height:1.3;scroll-margin-top:90px}\n.dd-post h3{margin:1.6em 0 .5em;font-size:1.22em;scroll-margin-top:90px}\n.dd-post a{color:var(--blue);text-decoration:underline;text-underline-offset:3px}\n.dd-post pre{background:#0f1430;color:#e6e9f5;padding:18px 20px;border-radius:12px;overflow-x:auto;line-height:1.6;border:1px solid #232a52;margin:1.2em 0;font-size:14.5px !important}\n.dd-post pre code{font-size:1em !important;background:none;color:inherit;padding:0;font-family:\"JetBrains Mono\",\"Fira Code\",Consolas,Menlo,monospace;white-space:pre}\n.dd-post :not(pre)>code{background:#eef1fb;color:#2b3a8f;padding:2px 6px;border-radius:5px;font-size:.9em}\n.dd-post .dd-file{display:inline-block;background:#151b3b;color:#c9d1ff;font:600 13px\/1 \"JetBrains Mono\",Consolas,monospace;padding:7px 12px;border-radius:8px 8px 0 0;margin:1em 0 -1.2em;position:relative}\n.dd-post .dd-hero{margin:0 0 1.6em;border-radius:16px;overflow:hidden;box-shadow:0 10px 30px rgba(11,16,32,.25)}\n.dd-post .dd-hero svg{display:block;width:100%;height:auto}\n.dd-post .dd-hero .node{transition:transform .25s,filter .25s;transform-box:fill-box;transform-origin:center;cursor:pointer}\n.dd-post .dd-hero .node:hover{transform:scale(1.08);filter:drop-shadow(0 0 10px #7c5cff)}\n.dd-post .dd-series{display:inline-flex;align-items:center;gap:8px;background:#151b3b;color:#dfe3ff;border-radius:999px;padding:6px 14px;font-size:.88em;font-weight:600;margin:0 0 .8em}\n.dd-post .dd-series span{background:var(--violet);color:#fff;border-radius:999px;padding:2px 10px}\n.dd-post .dd-prereq{font-size:.96em;color:var(--muted);border-left:3px solid var(--line);padding-left:12px;margin:.6em 0 1.2em}\n.dd-post .dd-summary{background:linear-gradient(135deg,#eef3ff,#f4efff);border:1px solid #d6dcf5;border-left:5px solid var(--violet);border-radius:12px;padding:18px 22px;margin:1.4em 0}\n.dd-post .dd-summary strong.lbl{display:block;font-size:1.05em;margin-bottom:6px;color:#3a2fa0}\n.dd-post .dd-summary ul{margin:.3em 0 0 1.1em}\n.dd-post .dd-toc{background:#f8f9fc;border:1px solid var(--line);border-radius:12px;padding:16px 22px;margin:1.4em 0}\n.dd-post .dd-toc ol{margin:.4em 0 0 1.2em;columns:2;column-gap:28px}\n.dd-post .dd-toc li{break-inside:avoid;margin:.15em 0}\n@media(max-width:640px){.dd-post .dd-toc ol{columns:1}}\n.dd-post .dd-tip,.dd-post .dd-note,.dd-post .dd-warn{border-radius:10px;padding:14px 18px;margin:1.3em 0}\n.dd-post .dd-tip{background:#ecfbf3;border-left:4px solid #1f9d61}\n.dd-post .dd-note{background:#eef4ff;border-left:4px solid var(--blue)}\n.dd-post .dd-warn{background:#fff6e8;border-left:4px solid #e08a00}\n.dd-post .dd-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px;margin:1.3em 0}\n.dd-post .dd-card{border:1px solid var(--line);border-radius:12px;padding:14px 16px;background:#fff;box-shadow:0 2px 8px rgba(20,30,70,.05)}\n.dd-post .dd-card b{display:block;color:#3a2fa0;margin-bottom:4px}\n.dd-post table{width:100%;border-collapse:collapse;margin:1.3em 0;font-size:.95em}\n.dd-post th,.dd-post td{border:1px solid var(--line);padding:10px 12px;text-align:left;vertical-align:top}\n.dd-post th{background:#151b3b;color:#fff}\n.dd-post tr:nth-child(even) td{background:#f8f9fc}\n.dd-post .dd-tablewrap{overflow-x:auto}\n.dd-post figure{margin:1.6em 0;text-align:center}\n.dd-post figure svg{width:100%;height:auto;border-radius:12px}\n.dd-post figcaption{font-size:.9em;color:var(--muted);margin-top:8px}\n.dd-post details{border:1px solid var(--line);border-radius:10px;padding:12px 16px;margin:.7em 0;background:#fff}\n.dd-post details summary{cursor:pointer;font-weight:600}\n.dd-post details[open] summary{margin-bottom:8px;color:#3a2fa0}\n.dd-post .dd-roadmap{border:2px solid #d6dcf5;background:#fbfbff}\n.dd-post .dd-roadmap h4{margin:1em 0 .3em;color:#151b3b;font-size:1em;text-transform:uppercase;letter-spacing:.04em}\n.dd-post .dd-roadmap ol{margin:.2em 0 .6em 1.4em;padding:0}\n.dd-post .dd-roadmap li{margin:.2em 0}\n.dd-post .dd-roadmap .here{background:var(--violet);color:#fff;border-radius:999px;padding:1px 9px;font-size:.8em;font-weight:700;margin-left:6px}\n.dd-post .dd-roadmap .soon{color:#8a90a6;font-size:.82em;margin-left:6px}\n.dd-post .dd-tabs{border:1px solid var(--line);border-radius:12px;padding:6px 16px 16px;margin:1.4em 0;background:#fbfbfe}\n.dd-post .dd-tabs>input{position:absolute;opacity:0;pointer-events:none}\n.dd-post .dd-tabs .labels label{display:inline-block;padding:8px 16px;margin:10px 6px 0 0;border-radius:999px;border:1px solid #cfd5ea;cursor:pointer;font-weight:600;font-size:.95em;background:#fff;transition:background .2s}\n.dd-post .dd-tabs .labels label:hover{border-color:var(--violet)}\n.dd-post .dd-tabs .panel{display:none;margin-top:12px}\n.dd-post .dd-tabs #ti1:checked~.labels label[for=ti1],.dd-post .dd-tabs #ti2:checked~.labels label[for=ti2],.dd-post .dd-tabs #ti3:checked~.labels label[for=ti3],.dd-post .dd-tabs #ti4:checked~.labels label[for=ti4]{background:var(--violet);color:#fff;border-color:var(--violet)}\n.dd-post .dd-tabs #ti1:checked~.p1,.dd-post .dd-tabs #ti2:checked~.p2,.dd-post .dd-tabs #ti3:checked~.p3,.dd-post .dd-tabs #ti4:checked~.p4{display:block}\n.dd-post .dd-tabs #ti1:focus-visible~.labels label[for=ti1],.dd-post .dd-tabs #ti2:focus-visible~.labels label[for=ti2],.dd-post .dd-tabs #ti3:focus-visible~.labels label[for=ti3],.dd-post .dd-tabs #ti4:focus-visible~.labels label[for=ti4]{outline:2px solid var(--blue);outline-offset:2px}\n.dd-post .dd-cta{background:linear-gradient(135deg,#0b1020,#151b3b);color:#e8ebff;border-radius:16px;padding:24px 26px;margin:2em 0;text-align:center}\n.dd-post .dd-cta h3{color:#fff;margin-top:0}\n.dd-post .dd-cta a{color:#a99bff}\n.dd-post .dd-vsc{position:relative;margin:1.5em 0;border-radius:12px;overflow:hidden;background:#1e1e1e;border:1px solid #2b2b2b;box-shadow:0 14px 34px rgba(0,0,0,.28);font-family:\"Segoe UI\",system-ui,sans-serif}\n.dd-post .dd-vsc>input{position:absolute;opacity:0;pointer-events:none}\n.dd-post .dd-vsc-bar{display:flex;align-items:center;gap:12px;background:#323233;color:#cccccc;font-size:12.5px;padding:8px 12px}\n.dd-post .dd-dots{display:inline-flex;gap:7px}.dd-post .dd-dots i{width:12px;height:12px;border-radius:50%;background:#ff5f57;display:block}.dd-post .dd-dots i:nth-child(2){background:#febc2e}.dd-post .dd-dots i:nth-child(3){background:#28c840}\n.dd-post .dd-vsc-title{flex:1;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:50px;opacity:.85}\n.dd-post .dd-vsc-tabs{display:flex;align-items:stretch;background:#252526;border-bottom:1px solid #1e1e1e;overflow-x:auto}\n.dd-post .dd-vsc-tabs label{display:flex;align-items:center;gap:7px;padding:9px 16px;color:#969696;font-size:13px;cursor:pointer;background:#2d2d2d;border-right:1px solid #252526;border-top:1px solid transparent;white-space:nowrap;margin:0;transition:color .15s}\n.dd-post .dd-vsc-tabs label:hover{color:#e0e0e0}\n.dd-post .dd-vsc-tabs .ic{font:700 9.5px\/1 Consolas,monospace;padding:3px 4px;border-radius:3px;color:#1e1e1e}\n.dd-post .ic.py{background:#ffd43b}.dd-post .ic.js{background:#f1e05a}.dd-post .ic.sh{background:#89e051}\n.dd-post .dd-vsc-tabs label.run{margin-left:auto;background:transparent;border:0;color:#89d185;font-weight:600;display:none}\n.dd-post .dd-vsc-tabs label.run:hover{color:#b5f0b0;background:#2a2d2e}\n.dd-post .dd-vsc-pane{display:none}\n.dd-post .dd-vsc>.f1:checked~.dd-vsc-tabs .t1,.dd-post .dd-vsc>.f2:checked~.dd-vsc-tabs .t2,.dd-post .dd-vsc>.f3:checked~.dd-vsc-tabs .t3{background:#1e1e1e;color:#ffffff;border-top-color:#3178c6}\n.dd-post .dd-vsc>.f1:checked~.p1,.dd-post .dd-vsc>.f2:checked~.p2,.dd-post .dd-vsc>.f3:checked~.p3{display:block}\n.dd-post .dd-vsc>.f1:checked~.dd-vsc-tabs .run1,.dd-post .dd-vsc>.f2:checked~.dd-vsc-tabs .run2,.dd-post .dd-vsc>.f3:checked~.dd-vsc-tabs .run3{display:flex}\n.dd-post .dd-vsc>.f1:focus-visible~.dd-vsc-tabs .t1,.dd-post .dd-vsc>.f2:focus-visible~.dd-vsc-tabs .t2,.dd-post .dd-vsc>.f3:focus-visible~.dd-vsc-tabs .t3,.dd-post .dd-vsc>input[type=checkbox]:focus-visible~.dd-vsc-tabs .run{outline:2px solid #3178c6;outline-offset:-2px}\n.dd-post .dd-vsc-crumbs{background:#1e1e1e;color:#a9a9a9;font-size:12px;padding:5px 16px 2px}\n.dd-post .dd-vsc pre.dd-vsc-code{background:#1e1e1e;color:#d4d4d4;border:0;border-radius:0;margin:0;padding:8px 0 14px;counter-reset:ln;line-height:1.62;overflow-x:auto}\n.dd-post .dd-vsc pre code{font-family:Consolas,\"JetBrains Mono\",\"Fira Code\",Menlo,monospace;color:inherit;background:none;white-space:pre}\n.dd-post .dd-vsc .l{display:inline-block;min-width:100%;padding-right:18px;box-sizing:border-box}\n.dd-post .dd-vsc .l::before{counter-increment:ln;content:counter(ln);display:inline-block;width:3.2em;padding-right:1.4em;text-align:right;color:#6e7681;user-select:none;-webkit-user-select:none}\n.dd-post .dd-vsc .l:hover{background:#2a2d2e}.dd-post .dd-vsc .l:hover::before{color:#c6c6c6}\n.dd-post .dd-vsc .k,.dd-post .dd-vsc .kn{color:#c586c0}.dd-post .dd-vsc .kc{color:#569cd6}.dd-post .dd-vsc .s{color:#ce9178}.dd-post .dd-vsc .c{color:#6a9955}.dd-post .dd-vsc .m{color:#b5cea8}.dd-post .dd-vsc .nf{color:#dcdcaa}.dd-post .dd-vsc .nc{color:#4ec9b0}.dd-post .dd-vsc .nb{color:#4ec9b0}.dd-post .dd-vsc .nd{color:#dcdcaa}.dd-post .dd-vsc .n{color:#9cdcfe}.dd-post .dd-vsc .o{color:#d4d4d4}.dd-post .dd-vsc .gp{color:#89d185;user-select:none;-webkit-user-select:none}\n.dd-post .dd-vsc-term{display:none;border-top:1px solid #3c3c3c;background:#181818}\n.dd-post .dd-vsc>.r1:checked~.p1 .dd-vsc-term,.dd-post .dd-vsc>.r2:checked~.p2 .dd-vsc-term,.dd-post .dd-vsc>.r3:checked~.p3 .dd-vsc-term{display:block;animation:ddterm .35s ease-out}\n@keyframes ddterm{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}\n.dd-post .dd-term-head{display:flex;gap:18px;align-items:center;font-size:11px;letter-spacing:.06em;color:#8b8b8b;padding:7px 16px 0}\n.dd-post .dd-term-head span:first-child{color:#e7e7e7;border-bottom:1px solid #e7e7e7;padding-bottom:4px}\n.dd-post .dd-term-head .dd-term-note{margin-left:auto;letter-spacing:0;text-transform:none;color:#d7ba7d;border:0;font-size:11.5px}\n.dd-post .dd-vsc-term pre{background:#181818;color:#cccccc;border:0;margin:0;padding:10px 16px 14px;border-radius:0}\n.dd-post .dd-vsc-status{display:flex;flex-wrap:wrap;gap:4px 14px;background:#007acc;color:#fff;font-size:11.5px;padding:4px 12px}\n.dd-post .dd-vsc-status .sp{flex:1}\n.dd-post .dd-vsc-termonly pre.dd-vsc-code{background:#181818;padding:12px 16px}\n@media(max-width:640px){.dd-post .dd-vsc-title{padding-right:0;text-align:left}.dd-post .dd-vsc-status span:nth-child(n+4){display:none}.dd-post .dd-vsc .l::before{width:2.2em;padding-right:1em}}\n.dd-post .dd-vsc-hint{font-size:.93em;color:var(--muted);margin-top:-.6em}\n.dd-post .dd-next{border:1px dashed var(--violet);border-radius:12px;padding:16px 20px;margin:1.6em 0;background:#f7f4ff}\n<\/style>\n\n<div class=\"dd-hero\">\n<svg viewBox=\"0 0 1200 520\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Generative AI series Part 3 banner: your Python or Node.js app sends a request with an API key to OpenAI, Claude and Gemini and gets back text and token usage\">\n<defs>\n<linearGradient id=\"ddbg\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#0b1020\"\/><stop offset=\"1\" stop-color=\"#151b3b\"\/><\/linearGradient>\n<linearGradient id=\"ddac\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\"><stop offset=\"0\" stop-color=\"#3178c6\"\/><stop offset=\"1\" stop-color=\"#7c5cff\"\/><\/linearGradient>\n<radialGradient id=\"ddglow\" cx=\".5\" cy=\".5\" r=\".5\"><stop offset=\"0\" stop-color=\"#7c5cff\" stop-opacity=\".45\"\/><stop offset=\"1\" stop-color=\"#7c5cff\" stop-opacity=\"0\"\/><\/radialGradient>\n<\/defs>\n<rect x=\"0\" y=\"-60\" width=\"1200\" height=\"640\" fill=\"url(#ddbg)\"\/>\n<circle cx=\"1000\" cy=\"110\" r=\"250\" fill=\"url(#ddglow)\"\/>\n<g opacity=\".12\" stroke=\"#9fb2ff\"><path d=\"M0 100H1200M0 200H1200M0 300H1200M0 400H1200M200 0V520M400 0V520M600 0V520M800 0V520M1000 0V520\"\/><\/g>\n<rect x=\"60\" y=\"46\" width=\"128\" height=\"38\" rx=\"19\" fill=\"url(#ddac)\"\/>\n<text x=\"124\" y=\"71\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#fff\">PART 3<\/text>\n<text x=\"204\" y=\"71\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" fill=\"#aab4e8\">Generative AI Series \u00b7 DevDojo<\/text>\n<text x=\"60\" y=\"148\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"50\" font-weight=\"800\" fill=\"#ffffff\">Your First LLM API Calls<\/text>\n<text x=\"60\" y=\"204\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"50\" font-weight=\"800\" fill=\"#ffffff\">in Python and Node.js<\/text>\n<text x=\"60\" y=\"248\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"24\" fill=\"#c9d1ff\">OpenAI, Claude and Gemini: keys, requests, tokens and errors<\/text>\n<path id=\"ddf1\" d=\"M250 395 C 380 395, 420 330, 560 330 S 800 330, 920 330\" fill=\"none\" stroke=\"url(#ddac)\" stroke-width=\"3\" stroke-dasharray=\"8 8\" opacity=\".8\"\/>\n<path id=\"ddf2\" d=\"M250 395 C 420 395, 560 395, 920 395\" fill=\"none\" stroke=\"url(#ddac)\" stroke-width=\"3\" stroke-dasharray=\"8 8\" opacity=\".8\"\/>\n<path id=\"ddf3\" d=\"M250 395 C 380 395, 420 460, 560 460 S 800 460, 920 460\" fill=\"none\" stroke=\"url(#ddac)\" stroke-width=\"3\" stroke-dasharray=\"8 8\" opacity=\".8\"\/>\n<g class=\"node\"><rect x=\"70\" y=\"350\" width=\"180\" height=\"90\" rx=\"16\" fill=\"url(#ddac)\"\/><text x=\"160\" y=\"388\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"22\" font-weight=\"800\" fill=\"#fff\">Your app<\/text><text x=\"160\" y=\"414\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"15\" fill=\"#eef\">Python \u00b7 Node.js<\/text><\/g>\n<g class=\"node\"><rect x=\"470\" y=\"372\" width=\"180\" height=\"46\" rx=\"12\" fill=\"#0b1020\" stroke=\"#7c5cff\" stroke-width=\"2\"\/><text x=\"560\" y=\"401\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"16\" font-weight=\"700\" fill=\"#c9d1ff\">\ud83d\udd11 API key + prompt<\/text><\/g>\n<g class=\"node\"><rect x=\"920\" y=\"306\" width=\"210\" height=\"48\" rx=\"12\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"1025\" y=\"337\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"19\" font-weight=\"700\" fill=\"#fff\">OpenAI<\/text><\/g>\n<g class=\"node\"><rect x=\"920\" y=\"371\" width=\"210\" height=\"48\" rx=\"12\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"1025\" y=\"402\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"19\" font-weight=\"700\" fill=\"#fff\">Anthropic Claude<\/text><\/g>\n<g class=\"node\"><rect x=\"920\" y=\"436\" width=\"210\" height=\"48\" rx=\"12\" fill=\"#1b2250\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"1025\" y=\"467\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"19\" font-weight=\"700\" fill=\"#fff\">Google Gemini<\/text><\/g>\n<circle r=\"7\" fill=\"#7c5cff\"><animateMotion dur=\"4s\" repeatCount=\"indefinite\"><mpath href=\"#ddf1\"\/><\/animateMotion><\/circle>\n<circle r=\"6\" fill=\"#3178c6\"><animateMotion dur=\"4s\" begin=\"1.3s\" repeatCount=\"indefinite\"><mpath href=\"#ddf2\"\/><\/animateMotion><\/circle>\n<circle r=\"5\" fill=\"#ffffff\"><animateMotion dur=\"4s\" begin=\"2.6s\" repeatCount=\"indefinite\"><mpath href=\"#ddf3\"\/><\/animateMotion><\/circle>\n<text x=\"60\" y=\"505\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"16\" fill=\"#7f89b5\">devdojo.co.in \u00b7 Beginner friendly \u00b7 Python + Node.js examples<\/text>\n<\/svg>\n<\/div>\n\n<div class=\"dd-series\">Series: Generative AI <span>Part 3 of 30<\/span><\/div>\n<p class=\"dd-prereq\"><strong>What you should know first:<\/strong> read <a href=\"https:\/\/devdojo.co.in\/?p=215\">Part 1: What Is Generative AI and How Do LLMs Work?<\/a> and <a href=\"https:\/\/devdojo.co.in\/?p=219\">Part 2: Tokens, Context Windows and Model Parameters<\/a>. You should know what a token is and that every model has a context window. Basic Python or JavaScript is enough.<\/p>\n\n<p>In the first two parts we learned how large language models think. Now it is time to write code. In this part you will make your first <strong>LLM API calls<\/strong> in Python and Node.js to the three most popular providers: <strong>OpenAI<\/strong>, <strong>Anthropic Claude<\/strong> and <strong>Google Gemini<\/strong>. You will create API keys safely, send a prompt, read the answer and the token usage, handle errors, and estimate cost. By the end, you will have one small helper that can talk to all three models with a single function call.<\/p>\n\n<div class=\"dd-summary\">\n<strong class=\"lbl\">Quick Summary<\/strong>\n<ul>\n<li>An <strong>LLM API call<\/strong> is an HTTPS request: you send a model name, instructions and a prompt with your secret <strong>API key<\/strong>, and get back generated text plus <strong>token usage<\/strong>.<\/li>\n<li>Install the official SDKs: <code>openai<\/code>, <code>anthropic<\/code>, <code>google-genai<\/code> (Python) and <code>openai<\/code>, <code>@anthropic-ai\/sdk<\/code>, <code>@google\/genai<\/code> (Node.js).<\/li>\n<li>Keep keys in a <code>.env<\/code> file, never in your code or Git. Each SDK reads its key from an environment variable automatically.<\/li>\n<li>The three APIs are similar but not the same: the system prompt, the required fields and where the answer text lives are all a little different.<\/li>\n<li>Always read the <code>usage<\/code> numbers, catch errors like 401 and 429, and keep model names in config so you can change them later.<\/li>\n<\/ul>\n<\/div>\n\n<details class=\"dd-roadmap\">\n<summary>Full Generative AI roadmap (30 parts)<\/summary>\n<h4>Beginner<\/h4>\n<ol start=\"1\">\n<li><a href=\"https:\/\/devdojo.co.in\/?p=215\">What Is Generative AI and How Do LLMs Work?<\/a><\/li>\n<li><a href=\"https:\/\/devdojo.co.in\/?p=219\">Tokens, Context Windows and Model Parameters<\/a><\/li>\n<li>Your First LLM API Calls in Python and Node.js (OpenAI, Anthropic, Gemini) <span class=\"here\">You are here<\/span><\/li>\n<li>Prompt Engineering Basics: Roles, Instructions and Examples <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Temperature, Top-p and Controlling Model Output <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Running Open Models Locally with Ollama <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Streaming Responses and Building a Simple Chat UI <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Structured Outputs: Getting Reliable JSON from LLMs <span class=\"soon\">Coming soon<\/span><\/li>\n<\/ol>\n<h4>Intermediate<\/h4>\n<ol start=\"9\">\n<li>Embeddings Explained: Turning Text into Vectors <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Vector Databases: pgvector, Chroma and Pinecone <span class=\"soon\">Coming soon<\/span><\/li>\n<li>RAG Fundamentals: Retrieval-Augmented Generation <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Document Loading and Chunking Strategies <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Better RAG: Hybrid Search, Re-ranking and Query Rewriting <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Function Calling and Tool Use <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Conversation Memory and Long Context Management <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Multimodal AI: Vision, Images and Audio Inputs <span class=\"soon\">Coming soon<\/span><\/li>\n<li>How Image Generation Works: Diffusion Models Basics <span class=\"soon\">Coming soon<\/span><\/li>\n<li>LangChain vs LlamaIndex: When to Use a Framework <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Testing LLM Apps: Evals, Metrics and Test Sets <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Cutting Cost and Latency: Caching, Batching and Model Choice <span class=\"soon\">Coming soon<\/span><\/li>\n<\/ol>\n<h4>Advanced<\/h4>\n<ol start=\"21\">\n<li>Fine-Tuning vs Prompting vs RAG: Choosing the Right Tool <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Fine-Tuning an Open Model with LoRA <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Transformers Deep Dive: Attention Explained Simply <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Reasoning Models and Thinking Time <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Guardrails, Safety and Prompt Injection Defence <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Monitoring and Tracing LLM Apps in Production <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Serving Open Models: vLLM, Quantization and GPUs <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Building a Production GenAI App End to End <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Responsible AI: Privacy, Licensing and Bias <span class=\"soon\">Coming soon<\/span><\/li>\n<li>Final Project: Build Your Own AI Knowledge Assistant <span class=\"soon\">Coming soon<\/span><\/li>\n<\/ol>\n<\/details>\n\n<nav class=\"dd-toc\" aria-label=\"Table of contents\">\n<strong>In this part<\/strong>\n<ol>\n<li><a href=\"#what-is-an-llm-api-call\">What is an LLM API call?<\/a><\/li>\n<li><a href=\"#api-keys\">Getting API keys safely<\/a><\/li>\n<li><a href=\"#setup\">Project setup<\/a><\/li>\n<li><a href=\"#openai\">Call OpenAI<\/a><\/li>\n<li><a href=\"#claude\">Call Anthropic Claude<\/a><\/li>\n<li><a href=\"#gemini\">Call Google Gemini<\/a><\/li>\n<li><a href=\"#compare\">Compare the three APIs<\/a><\/li>\n<li><a href=\"#one-helper\">One helper for all providers<\/a><\/li>\n<li><a href=\"#errors\">Handling errors and retries<\/a><\/li>\n<li><a href=\"#cost\">Estimating cost from usage<\/a><\/li>\n<li><a href=\"#common-errors\">Common errors and fixes<\/a><\/li>\n<li><a href=\"#best-practices\">Best practices<\/a><\/li>\n<li><a href=\"#practice\">Practice task<\/a><\/li>\n<li><a href=\"#faq\">FAQ<\/a><\/li>\n<\/ol>\n<\/nav>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"what-is-an-llm-api-call\">What is an LLM API call?<\/h2>\n<p>When you chat in ChatGPT or Claude.ai, a web app sends your message to a model running on the provider&#8217;s servers. An <strong>API<\/strong> (Application Programming Interface) lets <em>your own program<\/em> do the same thing. Instead of typing in a chat box, your code sends an HTTPS request and gets the model&#8217;s answer back as data you can use: show it on a website, save it to a database, or pass it to another function.<\/p>\n<p>Every LLM API call has the same basic parts:<\/p>\n<div class=\"dd-cards\">\n<div class=\"dd-card\"><b>1. API key<\/b>A secret string that proves who you are. The provider uses it to allow the request and to bill your account.<\/div>\n<div class=\"dd-card\"><b>2. Model name<\/b>Which model should answer, for example a small fast model for simple tasks or a bigger one for hard tasks.<\/div>\n<div class=\"dd-card\"><b>3. Instructions + prompt<\/b>The <em>system<\/em> instructions (how to behave) and the <em>user<\/em> message (what to do).<\/div>\n<div class=\"dd-card\"><b>4. Limits<\/b>Settings like the maximum output tokens, so a long answer does not surprise your bill.<\/div>\n<\/div>\n<p>The response comes back as JSON. The SDK turns it into an object with the generated <strong>text<\/strong>, the <strong>token usage<\/strong> (input and output tokens, which you learned about in Part 2), and a <strong>stop reason<\/strong> telling you why the model stopped writing.<\/p>\n\n<figure>\n<svg viewBox=\"0 0 1000 300\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Diagram of an LLM API call: your code uses an SDK to send an HTTPS request with an API key, model, instructions and prompt; the provider returns JSON with text, usage and stop reason\">\n<rect width=\"1000\" height=\"300\" fill=\"#f6f7fc\"\/>\n<defs><marker id=\"ar1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#3178c6\"\/><\/marker><marker id=\"ar2\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"8\" markerHeight=\"8\" orient=\"auto\"><path d=\"M0 0L10 5L0 10z\" fill=\"#7c5cff\"\/><\/marker><\/defs>\n<rect x=\"30\" y=\"90\" width=\"170\" height=\"110\" rx=\"14\" fill=\"#151b3b\"\/><text x=\"115\" y=\"135\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"19\" font-weight=\"700\" fill=\"#fff\">Your code<\/text><text x=\"115\" y=\"162\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#aab4e8\">app.py \/ app.mjs<\/text>\n<rect x=\"250\" y=\"105\" width=\"140\" height=\"80\" rx=\"12\" fill=\"#fff\" stroke=\"#3178c6\" stroke-width=\"2\"\/><text x=\"320\" y=\"140\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"17\" font-weight=\"700\" fill=\"#151b3b\">SDK<\/text><text x=\"320\" y=\"163\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"13\" fill=\"#5b6275\">openai \/ anthropic \/ genai<\/text>\n<path d=\"M200 145H245\" stroke=\"#3178c6\" stroke-width=\"3\" marker-end=\"url(#ar1)\"\/>\n<path d=\"M390 125 C 500 60, 620 60, 735 110\" fill=\"none\" stroke=\"#3178c6\" stroke-width=\"3\" marker-end=\"url(#ar1)\"\/>\n<rect x=\"440\" y=\"28\" width=\"250\" height=\"56\" rx=\"10\" fill=\"#eef4ff\" stroke=\"#3178c6\"\/><text x=\"565\" y=\"51\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" font-weight=\"700\" fill=\"#151b3b\">HTTPS request<\/text><text x=\"565\" y=\"72\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"13\" fill=\"#5b6275\">API key \u00b7 model \u00b7 instructions \u00b7 prompt<\/text>\n<rect x=\"740\" y=\"90\" width=\"230\" height=\"110\" rx=\"14\" fill=\"#7c5cff\"\/><text x=\"855\" y=\"137\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"19\" font-weight=\"700\" fill=\"#fff\">Provider servers<\/text><text x=\"855\" y=\"163\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" fill=\"#efeaff\">the LLM runs here<\/text>\n<path d=\"M735 185 C 620 240, 500 240, 395 170\" fill=\"none\" stroke=\"#7c5cff\" stroke-width=\"3\" marker-end=\"url(#ar2)\"\/>\n<rect x=\"440\" y=\"214\" width=\"250\" height=\"56\" rx=\"10\" fill=\"#f4efff\" stroke=\"#7c5cff\"\/><text x=\"565\" y=\"237\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"14\" font-weight=\"700\" fill=\"#151b3b\">JSON response<\/text><text x=\"565\" y=\"258\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"13\" fill=\"#5b6275\">text \u00b7 usage (tokens) \u00b7 stop reason<\/text>\n<\/svg>\n<figcaption>Figure 1: The life of one LLM API call. The SDK builds the HTTPS request and turns the JSON reply into an easy object.<\/figcaption>\n<\/figure>\n\n<div class=\"dd-note\"><strong>Why use the SDK instead of raw HTTP?<\/strong> You can call these APIs with plain <code>fetch<\/code> or <code>requests<\/code>, but the official SDKs add types, automatic retries, timeouts, clear error classes and helper fields like <code>output_text<\/code>. For beginners they remove a lot of small mistakes.<\/div>\n\n<h2 id=\"api-keys\">Getting API keys safely<\/h2>\n<p>Each provider has its own developer console where you create a key:<\/p>\n<ul>\n<li><strong>OpenAI:<\/strong> the OpenAI Platform dashboard \u2192 API keys. Add a small amount of billing credit first.<\/li>\n<li><strong>Anthropic:<\/strong> the Claude Console \u2192 API keys. New accounts usually need to add credit before calls work.<\/li>\n<li><strong>Google Gemini:<\/strong> Google AI Studio \u2192 Get API key. Gemini has a free tier with rate limits, which is great for learning.<\/li>\n<\/ul>\n<div class=\"dd-warn\"><strong>Treat an API key like a password.<\/strong> Anyone who has it can spend your money. Never paste it into code, screenshots, GitHub, or front-end JavaScript that runs in the browser. If a key leaks, delete it in the console and create a new one right away.<\/div>\n<p>The safe pattern is to store keys in a <code>.env<\/code> file that stays on your machine and is listed in <code>.gitignore<\/code>. Commit a <code>.env.example<\/code> with fake values, so teammates know which variables they need. The small script below checks which keys are set without ever printing the full key.<\/p>\n\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: .env.example, check_keys.py\">\n<input type=\"radio\" name=\"v1\" id=\"v1f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v1\" id=\"v1f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v1r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">2 files \u2014 devdojo-llm-api \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v1f1\" class=\"t1\"><b class=\"ic sh\">$_<\/b>.env.example<\/label>\n<label for=\"v1f2\" class=\"t2\"><b class=\"ic py\">PY<\/b>check_keys.py<\/label>\n<label for=\"v1r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a .env.example<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># Copy this file to .env and paste your own keys.<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># Never commit .env to Git!<\/span><\/span>\n<span class=\"l\"><span class=\"n\">OPENAI_API_KEY<\/span>=<span class=\"s\">sk-your-openai-key<\/span><\/span>\n<span class=\"l\"><span class=\"n\">ANTHROPIC_API_KEY<\/span>=<span class=\"s\">sk-ant-your-anthropic-key<\/span><\/span>\n<span class=\"l\"><span class=\"n\">GEMINI_API_KEY<\/span>=<span class=\"s\">your-gemini-key<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># Optional: change models without touching code<\/span><\/span>\n<span class=\"l\"><span class=\"n\">OPENAI_MODEL<\/span>=<span class=\"s\">gpt-6-luna<\/span><\/span>\n<span class=\"l\"><span class=\"n\">ANTHROPIC_MODEL<\/span>=<span class=\"s\">claude-haiku-5-5<\/span><\/span>\n<span class=\"l\"><span class=\"n\">GEMINI_MODEL<\/span>=<span class=\"s\">gemini-3.8-flash<\/span><\/span><\/code><\/pre>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 10, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>Properties<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a check_keys.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># pip install python-dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">os<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">dotenv<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">load_dotenv<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">load_dotenv<\/span>()  <span class=\"c\"># reads the .env file in this folder<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">KEYS<\/span> = [<span class=\"s\">\"OPENAI_API_KEY\"<\/span>, <span class=\"s\">\"ANTHROPIC_API_KEY\"<\/span>, <span class=\"s\">\"GEMINI_API_KEY\"<\/span>]<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">for<\/span> <span class=\"n\">name<\/span> in <span class=\"n\">KEYS<\/span>:<\/span>\n<span class=\"l\">    <span class=\"n\">value<\/span> = <span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"n\">name<\/span>)<\/span>\n<span class=\"l\">    <span class=\"k\">if<\/span> <span class=\"n\">value<\/span>:<\/span>\n<span class=\"l\">        <span class=\"c\"># never print the full key - show only the last 4 characters<\/span><\/span>\n<span class=\"l\">        <span class=\"nb\">print<\/span>(<span class=\"s\">f\"{<\/span><span class=\"n\">name<\/span><span class=\"s\">:&lt;18} found  (...{<\/span><span class=\"n\">value<\/span>[-<span class=\"m\">4<\/span>:]<span class=\"s\">})\"<\/span>)<\/span>\n<span class=\"l\">    <span class=\"k\">else<\/span>:<\/span>\n<span class=\"l\">        <span class=\"nb\">print<\/span>(<span class=\"s\">f\"{<\/span><span class=\"n\">name<\/span><span class=\"s\">:&lt;18} missing - add it to your .env file\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output \u2013 Python 3.13, only an OpenAI test key set<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python check_keys.py\nOPENAI_API_KEY     found  (...7f3a)\nANTHROPIC_API_KEY  missing - add it to your .env file\nGEMINI_API_KEY     missing - add it to your .env file<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 15, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click the tabs to switch files, then press <strong>\u25b6 Run<\/strong> on <code>check_keys.py<\/code> to see the output.<\/p>\n\n<h2 id=\"setup\">Project setup for Python and Node.js<\/h2>\n<p>Create one folder for this whole part. We will install the three official SDKs for both languages. For Node.js you need version 20.6 or newer, because we use the built-in <code>--env-file<\/code> flag to load the <code>.env<\/code> file (no extra package needed).<\/p>\n\n<div class=\"dd-vsc dd-vsc-termonly\" role=\"group\" aria-label=\"Terminal: devdojo-llm-api\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">devdojo-llm-api \u2014 bash<\/span><\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># 1. Create a project folder<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> mkdir devdojo-llm-api &amp;&amp; cd devdojo-llm-api<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># 2. Python: create a virtual environment and install the three SDKs<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> python -m venv .venv<\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> source .venv\/bin\/activate      # Windows: .venv\\Scripts\\activate<\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> pip install openai anthropic google-genai python-dotenv<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># 3. Node.js (version 20.6 or newer): install the same three SDKs<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> npm init -y<\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> npm pkg set type=module<\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> npm install openai @anthropic-ai\/sdk @google\/genai<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># 4. Keep secrets out of Git<\/span><\/span>\n<span class=\"l\"><span class=\"gp\">$<\/span> echo &quot;.env&quot; &gt;&gt; .gitignore<\/span><\/code><\/pre>\n<\/div>\n\n<div class=\"dd-tip\"><strong>Model names change often.<\/strong> Providers release new models every few months and retire old ones. That is why every example reads the model name from an environment variable (like <code>OPENAI_MODEL<\/code>) with a sensible default. When a newer model arrives, you change one line in <code>.env<\/code>, not your code. Check each provider&#8217;s models page for the current list.<\/div>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"openai\">Your first OpenAI API call<\/h2>\n<p>OpenAI recommends the <strong>Responses API<\/strong> (<code>client.responses.create<\/code>) for new projects. You pass the model, optional <code>instructions<\/code> (the system prompt) and the <code>input<\/code> (the user message). The SDK gives you a handy <code>output_text<\/code> field with the whole answer as one string, and <code>usage<\/code> with the token counts.<\/p>\n\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: openai_hello.py, openai-hello.mjs\">\n<input type=\"radio\" name=\"v2\" id=\"v2f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v2\" id=\"v2f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v2r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v2r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">2 files \u2014 devdojo-llm-api \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v2f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>openai_hello.py<\/label>\n<label for=\"v2f2\" class=\"t2\"><b class=\"ic js\">JS<\/b>openai-hello.mjs<\/label>\n<label for=\"v2r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v2r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a openai_hello.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># pip install openai python-dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">os<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">dotenv<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">load_dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">openai<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">OpenAI<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">load_dotenv<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">client<\/span> = <span class=\"n\">OpenAI<\/span>()  <span class=\"c\"># reads OPENAI_API_KEY from the environment<\/span><\/span>\n<span class=\"l\"><span class=\"n\">model<\/span> = <span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"OPENAI_MODEL\"<\/span>, <span class=\"s\">\"gpt-6-luna\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">response<\/span> = <span class=\"n\">client<\/span>.<span class=\"n\">responses<\/span>.<span class=\"n\">create<\/span>(<\/span>\n<span class=\"l\">    <span class=\"n\">model<\/span>=<span class=\"n\">model<\/span>,<\/span>\n<span class=\"l\">    <span class=\"n\">instructions<\/span>=<span class=\"s\">\"You are a friendly coding teacher. Answer in 2 short sentences.\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"nb\">input<\/span>=<span class=\"s\">\"What is an API key?\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"n\">max_output_tokens<\/span>=<span class=\"m\">200<\/span>,<\/span>\n<span class=\"l\">)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">response<\/span>.<span class=\"n\">output_text<\/span>)<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"tokens in:\"<\/span>, <span class=\"n\">response<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">input_tokens<\/span>,<\/span>\n<span class=\"l\">      <span class=\"s\">\"| tokens out:\"<\/span>, <span class=\"n\">response<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">output_tokens<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your text and numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python openai_hello.py\nAn API key is a secret password that tells the API who you are.\nThe service uses it to allow your requests and to bill your account.\ntokens in: &lt;about 40&gt; | tokens out: &lt;about 30&gt;<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 19, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a openai-hello.mjs<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\">\/\/ npm install openai<\/span><\/span>\n<span class=\"l\"><span class=\"c\">\/\/ run with: node --env-file=.env openai-hello.mjs<\/span><\/span>\n<span class=\"l\"><span class=\"k\">import<\/span> <span class=\"n\">OpenAI<\/span> <span class=\"k\">from<\/span> <span class=\"s\">\"openai\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">client<\/span> = new <span class=\"n\">OpenAI<\/span>(); <span class=\"c\">\/\/ reads OPENAI_API_KEY from the environment<\/span><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">model<\/span> = <span class=\"n\">process<\/span>.<span class=\"n\">env<\/span>.<span class=\"n\">OPENAI_MODEL<\/span> ?? <span class=\"s\">\"gpt-6-luna\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">response<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">client<\/span>.<span class=\"n\">responses<\/span>.<span class=\"n\">create<\/span>({<\/span>\n<span class=\"l\">  <span class=\"n\">model<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">instructions<\/span>: <span class=\"s\">\"You are a friendly coding teacher. Answer in 2 short sentences.\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">input<\/span>: <span class=\"s\">\"What is an API key?\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">max_output_tokens<\/span>: <span class=\"m\">200<\/span>,<\/span>\n<span class=\"l\">});<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"n\">response<\/span>.<span class=\"n\">output_text<\/span>);<\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"tokens in:\"<\/span>, <span class=\"n\">response<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">input_tokens<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"| tokens out:\"<\/span>, <span class=\"n\">response<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">output_tokens<\/span>);<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your text and numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> node --env-file=.env openai-hello.mjs\nAn API key is a secret password that tells the API who you are.\nThe service uses it to allow your requests and to bill your account.\ntokens in: &lt;about 40&gt; | tokens out: &lt;about 30&gt;<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 17, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>JavaScript<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Switch between Python and Node.js with the tabs, then press <strong>\u25b6 Run<\/strong>.<\/p>\n\n<p>Notice three things. First, <code>OpenAI()<\/code> needs no arguments, because it reads <code>OPENAI_API_KEY<\/code> from the environment. Second, <code>max_output_tokens<\/code> puts a ceiling on the answer length (and cost). Third, the usage numbers are exactly the input and output tokens you are billed for. You can read more in the official <a href=\"https:\/\/developers.openai.com\/api\/docs\/guides\/text\" target=\"_blank\" rel=\"noopener\">OpenAI text generation guide<\/a>.<\/p>\n<div class=\"dd-note\">You may also see older tutorials using <code>client.chat.completions.create<\/code> with a <code>messages<\/code> list. The Chat Completions API still works, but the Responses API is the newer recommended way and is what we use in this series.<\/div>\n\n<h2 id=\"claude\">Your first Anthropic Claude API call<\/h2>\n<p>Claude uses the <strong>Messages API<\/strong> (<code>client.messages.create<\/code>). It looks a lot like a chat: you send a list of <code>messages<\/code>, each with a <code>role<\/code> (<code>\"user\"<\/code> or <code>\"assistant\"<\/code>) and <code>content<\/code>. Two differences matter for beginners: the system prompt goes in a separate <code>system<\/code> field, and <code>max_tokens<\/code> is <strong>required<\/strong>.<\/p>\n\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: claude_hello.py, claude-hello.mjs\">\n<input type=\"radio\" name=\"v3\" id=\"v3f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v3\" id=\"v3f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v3r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v3r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">2 files \u2014 devdojo-llm-api \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v3f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>claude_hello.py<\/label>\n<label for=\"v3f2\" class=\"t2\"><b class=\"ic js\">JS<\/b>claude-hello.mjs<\/label>\n<label for=\"v3r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v3r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a claude_hello.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># pip install anthropic python-dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">os<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">dotenv<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">load_dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">anthropic<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">load_dotenv<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">client<\/span> = <span class=\"n\">anthropic<\/span>.<span class=\"n\">Anthropic<\/span>()  <span class=\"c\"># reads ANTHROPIC_API_KEY<\/span><\/span>\n<span class=\"l\"><span class=\"n\">model<\/span> = <span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"ANTHROPIC_MODEL\"<\/span>, <span class=\"s\">\"claude-haiku-5-5\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">message<\/span> = <span class=\"n\">client<\/span>.<span class=\"n\">messages<\/span>.<span class=\"n\">create<\/span>(<\/span>\n<span class=\"l\">    <span class=\"n\">model<\/span>=<span class=\"n\">model<\/span>,<\/span>\n<span class=\"l\">    <span class=\"n\">max_tokens<\/span>=<span class=\"m\">200<\/span>,  <span class=\"c\"># required for Claude<\/span><\/span>\n<span class=\"l\">    <span class=\"n\">system<\/span>=<span class=\"s\">\"You are a friendly coding teacher. Answer in 2 short sentences.\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"n\">messages<\/span>=[<\/span>\n<span class=\"l\">        {<span class=\"s\">\"role\"<\/span>: <span class=\"s\">\"user\"<\/span>, <span class=\"s\">\"content\"<\/span>: <span class=\"s\">\"What is an API key?\"<\/span>},<\/span>\n<span class=\"l\">    ],<\/span>\n<span class=\"l\">)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">message<\/span>.<span class=\"n\">content<\/span>[<span class=\"m\">0<\/span>].<span class=\"n\">text<\/span>)<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"tokens in:\"<\/span>, <span class=\"n\">message<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">input_tokens<\/span>,<\/span>\n<span class=\"l\">      <span class=\"s\">\"| tokens out:\"<\/span>, <span class=\"n\">message<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">output_tokens<\/span>)<\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"stop reason:\"<\/span>, <span class=\"n\">message<\/span>.<span class=\"n\">stop_reason<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your text and numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python claude_hello.py\nAn API key is a secret code that identifies your app to a service.\nKeep it private, because anyone who has it can use your account.\ntokens in: &lt;about 35&gt; | tokens out: &lt;about 30&gt;\nstop reason: end_turn<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 22, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a claude-hello.mjs<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\">\/\/ npm install @anthropic-ai\/sdk<\/span><\/span>\n<span class=\"l\"><span class=\"c\">\/\/ run with: node --env-file=.env claude-hello.mjs<\/span><\/span>\n<span class=\"l\"><span class=\"k\">import<\/span> <span class=\"n\">Anthropic<\/span> <span class=\"k\">from<\/span> <span class=\"s\">\"@anthropic-ai\/sdk\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">client<\/span> = new <span class=\"n\">Anthropic<\/span>(); <span class=\"c\">\/\/ reads ANTHROPIC_API_KEY<\/span><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">model<\/span> = <span class=\"n\">process<\/span>.<span class=\"n\">env<\/span>.<span class=\"n\">ANTHROPIC_MODEL<\/span> ?? <span class=\"s\">\"claude-haiku-5-5\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">message<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">client<\/span>.<span class=\"n\">messages<\/span>.<span class=\"n\">create<\/span>({<\/span>\n<span class=\"l\">  <span class=\"n\">model<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">max_tokens<\/span>: <span class=\"m\">200<\/span>, <span class=\"c\">\/\/ required for Claude<\/span><\/span>\n<span class=\"l\">  <span class=\"n\">system<\/span>: <span class=\"s\">\"You are a friendly coding teacher. Answer in 2 short sentences.\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">messages<\/span>: [{ <span class=\"n\">role<\/span>: <span class=\"s\">\"user\"<\/span>, <span class=\"n\">content<\/span>: <span class=\"s\">\"What is an API key?\"<\/span> }],<\/span>\n<span class=\"l\">});<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"n\">message<\/span>.<span class=\"n\">content<\/span>[<span class=\"m\">0<\/span>].<span class=\"n\">text<\/span>);<\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"tokens in:\"<\/span>, <span class=\"n\">message<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">input_tokens<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"| tokens out:\"<\/span>, <span class=\"n\">message<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">output_tokens<\/span>);<\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"stop reason:\"<\/span>, <span class=\"n\">message<\/span>.<span class=\"n\">stop_reason<\/span>);<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your text and numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> node --env-file=.env claude-hello.mjs\nAn API key is a secret code that identifies your app to a service.\nKeep it private, because anyone who has it can use your account.\ntokens in: &lt;about 35&gt; | tokens out: &lt;about 30&gt;\nstop reason: end_turn<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 18, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>JavaScript<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Same request, two languages. Notice that <code>max_tokens<\/code> is required for Claude.<\/p>\n\n<p>The answer lives in <code>message.content<\/code>, which is a <em>list of content blocks<\/em>. For a simple text reply, the first block is a text block, so <code>content[0].text<\/code> works. Later in the series, when we use tools and images, you will see other block types too. The <code>stop_reason<\/code> tells you why Claude stopped: <code>end_turn<\/code> means it finished normally, while <code>max_tokens<\/code> means it hit your limit and the answer is cut off. See the <a href=\"https:\/\/platform.claude.com\/docs\/en\/api\/messages\" target=\"_blank\" rel=\"noopener\">Anthropic Messages API reference<\/a> for every option.<\/p>\n\n<h2 id=\"gemini\">Your first Google Gemini API call<\/h2>\n<p>Google&#8217;s official SDK is the <strong>Google Gen AI SDK<\/strong>: <code>google-genai<\/code> for Python and <code>@google\/genai<\/code> for JavaScript. You create a client and call <code>models.generate_content<\/code> (Python) or <code>models.generateContent<\/code> (Node.js). The system prompt and limits go inside a <code>config<\/code> object.<\/p>\n\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: gemini_hello.py, gemini-hello.mjs\">\n<input type=\"radio\" name=\"v4\" id=\"v4f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v4\" id=\"v4f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v4r1\" class=\"r1\">\n<input type=\"checkbox\" id=\"v4r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">2 files \u2014 devdojo-llm-api \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v4f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>gemini_hello.py<\/label>\n<label for=\"v4f2\" class=\"t2\"><b class=\"ic js\">JS<\/b>gemini-hello.mjs<\/label>\n<label for=\"v4r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<label for=\"v4r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a gemini_hello.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># pip install google-genai python-dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">os<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">dotenv<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">load_dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">google<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">genai<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">google.genai<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">types<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">load_dotenv<\/span>()<\/span>\n<span class=\"l\"><span class=\"n\">client<\/span> = <span class=\"n\">genai<\/span>.<span class=\"n\">Client<\/span>()  <span class=\"c\"># reads GEMINI_API_KEY<\/span><\/span>\n<span class=\"l\"><span class=\"n\">model<\/span> = <span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"GEMINI_MODEL\"<\/span>, <span class=\"s\">\"gemini-3.8-flash\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">response<\/span> = <span class=\"n\">client<\/span>.<span class=\"n\">models<\/span>.<span class=\"n\">generate_content<\/span>(<\/span>\n<span class=\"l\">    <span class=\"n\">model<\/span>=<span class=\"n\">model<\/span>,<\/span>\n<span class=\"l\">    <span class=\"n\">contents<\/span>=<span class=\"s\">\"What is an API key?\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"n\">config<\/span>=<span class=\"n\">types<\/span>.<span class=\"n\">GenerateContentConfig<\/span>(<\/span>\n<span class=\"l\">        <span class=\"n\">system_instruction<\/span>=<span class=\"s\">\"You are a friendly coding teacher. Answer in 2 short sentences.\"<\/span>,<\/span>\n<span class=\"l\">        <span class=\"n\">max_output_tokens<\/span>=<span class=\"m\">200<\/span>,<\/span>\n<span class=\"l\">    ),<\/span>\n<span class=\"l\">)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"n\">response<\/span>.<span class=\"n\">text<\/span>)<\/span>\n<span class=\"l\"><span class=\"n\">usage<\/span> = <span class=\"n\">response<\/span>.<span class=\"n\">usage_metadata<\/span><\/span>\n<span class=\"l\"><span class=\"nb\">print<\/span>(<span class=\"s\">\"tokens in:\"<\/span>, <span class=\"n\">usage<\/span>.<span class=\"n\">prompt_token_count<\/span>,<\/span>\n<span class=\"l\">      <span class=\"s\">\"| tokens out:\"<\/span>, <span class=\"n\">usage<\/span>.<span class=\"n\">candidates_token_count<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your text and numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python gemini_hello.py\nAn API key is a unique string that proves your program is allowed to use an API.\nThink of it like a library card for software.\ntokens in: &lt;about 30&gt; | tokens out: &lt;about 30&gt;<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 23, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a gemini-hello.mjs<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\">\/\/ npm install @google\/genai<\/span><\/span>\n<span class=\"l\"><span class=\"c\">\/\/ run with: node --env-file=.env gemini-hello.mjs<\/span><\/span>\n<span class=\"l\"><span class=\"k\">import<\/span> { <span class=\"n\">GoogleGenAI<\/span> } <span class=\"k\">from<\/span> <span class=\"s\">\"@google\/genai\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">ai<\/span> = new <span class=\"n\">GoogleGenAI<\/span>({}); <span class=\"c\">\/\/ reads GEMINI_API_KEY<\/span><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">model<\/span> = <span class=\"n\">process<\/span>.<span class=\"n\">env<\/span>.<span class=\"n\">GEMINI_MODEL<\/span> ?? <span class=\"s\">\"gemini-3.8-flash\"<\/span>;<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">response<\/span> = <span class=\"k\">await<\/span> <span class=\"n\">ai<\/span>.<span class=\"n\">models<\/span>.<span class=\"n\">generateContent<\/span>({<\/span>\n<span class=\"l\">  <span class=\"n\">model<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">contents<\/span>: <span class=\"s\">\"What is an API key?\"<\/span>,<\/span>\n<span class=\"l\">  <span class=\"n\">config<\/span>: {<\/span>\n<span class=\"l\">    <span class=\"n\">systemInstruction<\/span>: <span class=\"s\">\"You are a friendly coding teacher. Answer in 2 short sentences.\"<\/span>,<\/span>\n<span class=\"l\">    <span class=\"n\">maxOutputTokens<\/span>: <span class=\"m\">200<\/span>,<\/span>\n<span class=\"l\">  },<\/span>\n<span class=\"l\">});<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"n\">response<\/span>.<span class=\"n\">text<\/span>);<\/span>\n<span class=\"l\"><span class=\"k\">const<\/span> <span class=\"n\">usage<\/span> = <span class=\"n\">response<\/span>.<span class=\"n\">usageMetadata<\/span>;<\/span>\n<span class=\"l\"><span class=\"n\">console<\/span>.<span class=\"n\">log<\/span>(<span class=\"s\">\"tokens in:\"<\/span>, <span class=\"n\">usage<\/span>.<span class=\"n\">promptTokenCount<\/span>,<\/span>\n<span class=\"l\">  <span class=\"s\">\"| tokens out:\"<\/span>, <span class=\"n\">usage<\/span>.<span class=\"n\">candidatesTokenCount<\/span>);<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your text and numbers will differ<\/span><\/div><pre><code><span class=\"gp\">$<\/span> node --env-file=.env gemini-hello.mjs\nAn API key is a unique string that proves your program is allowed to use an API.\nThink of it like a library card for software.\ntokens in: &lt;about 30&gt; | tokens out: &lt;about 30&gt;<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 20, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 2<\/span><span>UTF-8<\/span><span>LF<\/span><span>JavaScript<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Click the tabs to compare the Python and Node.js versions.<\/p>\n\n<p>Gemini gives you the full answer in <code>response.text<\/code>. Token counts are in <code>usage_metadata<\/code> with different names: <code>prompt_token_count<\/code> for input and <code>candidates_token_count<\/code> for output. Google also offers a newer Interactions API in the same SDK; the <code>generate_content<\/code> call shown here is the classic, widely used one and is great for learning. The <a href=\"https:\/\/ai.google.dev\/gemini-api\/docs\" target=\"_blank\" rel=\"noopener\">Gemini API docs<\/a> show both.<\/p>\n<div class=\"dd-warn\"><strong>Older Gemini tutorials<\/strong> use the package <code>google-generativeai<\/code> and <code>genai.GenerativeModel(...)<\/code>. That is the old SDK. For new code, use <code>google-genai<\/code> as shown above.<\/div>\n\n<h2 id=\"compare\">OpenAI vs Claude vs Gemini: comparing the request formats<\/h2>\n<p>All three APIs do the same job, but the field names are different. Click the tabs below to see the important details of each one side by side.<\/p>\n\n<div class=\"dd-tabs\" role=\"group\" aria-label=\"Compare LLM API request formats\">\n<input type=\"radio\" name=\"ddcmp\" id=\"ti1\" checked>\n<input type=\"radio\" name=\"ddcmp\" id=\"ti2\">\n<input type=\"radio\" name=\"ddcmp\" id=\"ti3\">\n<input type=\"radio\" name=\"ddcmp\" id=\"ti4\">\n<div class=\"labels\"><label for=\"ti1\">OpenAI<\/label><label for=\"ti2\">Anthropic Claude<\/label><label for=\"ti3\">Google Gemini<\/label><label for=\"ti4\">Quick table<\/label><\/div>\n<div class=\"panel p1\">\n<ul>\n<li><strong>Method:<\/strong> <code>client.responses.create(...)<\/code><\/li>\n<li><strong>System prompt:<\/strong> <code>instructions=\"...\"<\/code><\/li>\n<li><strong>User message:<\/strong> <code>input=\"...\"<\/code> (a string, or a list of messages)<\/li>\n<li><strong>Length limit:<\/strong> <code>max_output_tokens<\/code> (optional)<\/li>\n<li><strong>Answer text:<\/strong> <code>response.output_text<\/code><\/li>\n<li><strong>Tokens:<\/strong> <code>usage.input_tokens<\/code>, <code>usage.output_tokens<\/code><\/li>\n<\/ul>\n<\/div>\n<div class=\"panel p2\">\n<ul>\n<li><strong>Method:<\/strong> <code>client.messages.create(...)<\/code><\/li>\n<li><strong>System prompt:<\/strong> <code>system=\"...\"<\/code> (separate from messages)<\/li>\n<li><strong>User message:<\/strong> <code>messages=[{\"role\": \"user\", \"content\": \"...\"}]<\/code><\/li>\n<li><strong>Length limit:<\/strong> <code>max_tokens<\/code> (<em>required<\/em>)<\/li>\n<li><strong>Answer text:<\/strong> <code>message.content[0].text<\/code><\/li>\n<li><strong>Tokens:<\/strong> <code>usage.input_tokens<\/code>, <code>usage.output_tokens<\/code><\/li>\n<\/ul>\n<\/div>\n<div class=\"panel p3\">\n<ul>\n<li><strong>Method:<\/strong> <code>client.models.generate_content(...)<\/code><\/li>\n<li><strong>System prompt:<\/strong> <code>config.system_instruction<\/code><\/li>\n<li><strong>User message:<\/strong> <code>contents=\"...\"<\/code><\/li>\n<li><strong>Length limit:<\/strong> <code>config.max_output_tokens<\/code> (optional)<\/li>\n<li><strong>Answer text:<\/strong> <code>response.text<\/code><\/li>\n<li><strong>Tokens:<\/strong> <code>usage_metadata.prompt_token_count<\/code>, <code>candidates_token_count<\/code><\/li>\n<\/ul>\n<\/div>\n<div class=\"panel p4\">\n<div class=\"dd-tablewrap\"><table>\n<tr><th>Detail<\/th><th>OpenAI<\/th><th>Claude<\/th><th>Gemini<\/th><\/tr>\n<tr><td>Key variable<\/td><td><code>OPENAI_API_KEY<\/code><\/td><td><code>ANTHROPIC_API_KEY<\/code><\/td><td><code>GEMINI_API_KEY<\/code><\/td><\/tr>\n<tr><td>Python package<\/td><td><code>openai<\/code><\/td><td><code>anthropic<\/code><\/td><td><code>google-genai<\/code><\/td><\/tr>\n<tr><td>Node.js package<\/td><td><code>openai<\/code><\/td><td><code>@anthropic-ai\/sdk<\/code><\/td><td><code>@google\/genai<\/code><\/td><\/tr>\n<tr><td>Max tokens required?<\/td><td>No<\/td><td>Yes<\/td><td>No<\/td><\/tr>\n<tr><td>Free tier for learning<\/td><td>No (prepaid credit)<\/td><td>No (prepaid credit)<\/td><td>Yes, with limits<\/td><\/tr>\n<\/table><\/div>\n<\/div>\n<\/div>\n\n<figure>\n<svg viewBox=\"0 0 1000 280\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-label=\"Diagram showing where the system prompt, user prompt and answer text go in OpenAI, Claude and Gemini\">\n<rect width=\"1000\" height=\"280\" fill=\"#f6f7fc\"\/>\n<text x=\"500\" y=\"34\" text-anchor=\"middle\" font-family=\"Segoe UI,Arial,sans-serif\" font-size=\"18\" font-weight=\"700\" fill=\"#151b3b\">Same idea, different field names<\/text>\n<g font-family=\"Segoe UI,Arial,sans-serif\">\n<text x=\"40\" y=\"96\" font-size=\"15\" font-weight=\"700\" fill=\"#5b6275\">System prompt<\/text>\n<text x=\"40\" y=\"156\" font-size=\"15\" font-weight=\"700\" fill=\"#5b6275\">User prompt<\/text>\n<text x=\"40\" y=\"216\" font-size=\"15\" font-weight=\"700\" fill=\"#5b6275\">Answer text<\/text>\n<text x=\"330\" y=\"64\" text-anchor=\"middle\" font-size=\"16\" font-weight=\"700\" fill=\"#3178c6\">OpenAI<\/text>\n<text x=\"590\" y=\"64\" text-anchor=\"middle\" font-size=\"16\" font-weight=\"700\" fill=\"#7c5cff\">Claude<\/text>\n<text x=\"850\" y=\"64\" text-anchor=\"middle\" font-size=\"16\" font-weight=\"700\" fill=\"#1f9d61\">Gemini<\/text>\n<rect x=\"215\" y=\"76\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#eef4ff\" stroke=\"#3178c6\"\/><text x=\"330\" y=\"98\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">instructions<\/text>\n<rect x=\"475\" y=\"76\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#f4efff\" stroke=\"#7c5cff\"\/><text x=\"590\" y=\"98\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">system<\/text>\n<rect x=\"735\" y=\"76\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#ecfbf3\" stroke=\"#1f9d61\"\/><text x=\"850\" y=\"98\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">config.system_instruction<\/text>\n<rect x=\"215\" y=\"136\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#eef4ff\" stroke=\"#3178c6\"\/><text x=\"330\" y=\"158\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">input<\/text>\n<rect x=\"475\" y=\"136\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#f4efff\" stroke=\"#7c5cff\"\/><text x=\"590\" y=\"158\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">messages[ {role: &#8220;user&#8221;} ]<\/text>\n<rect x=\"735\" y=\"136\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#ecfbf3\" stroke=\"#1f9d61\"\/><text x=\"850\" y=\"158\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">contents<\/text>\n<rect x=\"215\" y=\"196\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#eef4ff\" stroke=\"#3178c6\"\/><text x=\"330\" y=\"218\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">response.output_text<\/text>\n<rect x=\"475\" y=\"196\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#f4efff\" stroke=\"#7c5cff\"\/><text x=\"590\" y=\"218\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">content[0].text<\/text>\n<rect x=\"735\" y=\"196\" width=\"230\" height=\"34\" rx=\"8\" fill=\"#ecfbf3\" stroke=\"#1f9d61\"\/><text x=\"850\" y=\"218\" text-anchor=\"middle\" font-size=\"14\" fill=\"#151b3b\">response.text<\/text>\n<text x=\"500\" y=\"262\" text-anchor=\"middle\" font-size=\"13\" fill=\"#5b6275\">Learn the pattern once, then only the names change.<\/text>\n<\/g>\n<\/svg>\n<figcaption>Figure 2: Where your instructions, your prompt and the answer live in each of the three APIs.<\/figcaption>\n<\/figure>\n\n<h2 id=\"one-helper\">One helper function for all three providers<\/h2>\n<p>In real projects you often want to try more than one model, for example to compare quality or to have a backup when one provider is down. Instead of copying three different code styles everywhere, wrap them in one small <code>ask()<\/code> function that always returns the same shape: <code>{\"text\", \"in\", \"out\"}<\/code>. The rest of your app never needs to know which provider answered.<\/p>\n\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: llm.py, compare.py\">\n<input type=\"radio\" name=\"v5\" id=\"v5f1\" class=\"f1\" checked>\n<input type=\"radio\" name=\"v5\" id=\"v5f2\" class=\"f2\">\n<input type=\"checkbox\" id=\"v5r2\" class=\"r2\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">2 files \u2014 devdojo-llm-api \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v5f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>llm.py<\/label>\n<label for=\"v5f2\" class=\"t2\"><b class=\"ic py\">PY<\/b>compare.py<\/label>\n<label for=\"v5r2\" class=\"run run2\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a llm.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># One small helper that talks to all three providers<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">os<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">dotenv<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">load_dotenv<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">load_dotenv<\/span>()<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">SYSTEM<\/span> = <span class=\"s\">\"You are a friendly coding teacher. Answer in 2 short sentences.\"<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">def<\/span> <span class=\"nf\">ask<\/span>(<span class=\"n\">provider<\/span>: <span class=\"nb\">str<\/span>, <span class=\"n\">prompt<\/span>: <span class=\"nb\">str<\/span>) -&gt; <span class=\"nb\">dict<\/span>:<\/span>\n<span class=\"l\">    <span class=\"s\">\"\"\"Send one prompt and return the text plus token usage.\"\"\"<\/span><\/span>\n<span class=\"l\">    <span class=\"k\">if<\/span> <span class=\"n\">provider<\/span> == <span class=\"s\">\"openai\"<\/span>:<\/span>\n<span class=\"l\">        <span class=\"kn\">from<\/span> <span class=\"n\">openai<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">OpenAI<\/span><\/span>\n<span class=\"l\">        <span class=\"n\">r<\/span> = <span class=\"n\">OpenAI<\/span>().<span class=\"n\">responses<\/span>.<span class=\"n\">create<\/span>(<\/span>\n<span class=\"l\">            <span class=\"n\">model<\/span>=<span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"OPENAI_MODEL\"<\/span>, <span class=\"s\">\"gpt-6-luna\"<\/span>),<\/span>\n<span class=\"l\">            <span class=\"n\">instructions<\/span>=<span class=\"n\">SYSTEM<\/span>, <span class=\"nb\">input<\/span>=<span class=\"n\">prompt<\/span>, <span class=\"n\">max_output_tokens<\/span>=<span class=\"m\">300<\/span>,<\/span>\n<span class=\"l\">        )<\/span>\n<span class=\"l\">        <span class=\"k\">return<\/span> {<span class=\"s\">\"text\"<\/span>: <span class=\"n\">r<\/span>.<span class=\"n\">output_text<\/span>,<\/span>\n<span class=\"l\">                <span class=\"s\">\"in\"<\/span>: <span class=\"n\">r<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">input_tokens<\/span>, <span class=\"s\">\"out\"<\/span>: <span class=\"n\">r<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">output_tokens<\/span>}<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\">    <span class=\"k\">if<\/span> <span class=\"n\">provider<\/span> == <span class=\"s\">\"anthropic\"<\/span>:<\/span>\n<span class=\"l\">        <span class=\"kn\">import<\/span> <span class=\"n\">anthropic<\/span><\/span>\n<span class=\"l\">        <span class=\"n\">r<\/span> = <span class=\"n\">anthropic<\/span>.<span class=\"n\">Anthropic<\/span>().<span class=\"n\">messages<\/span>.<span class=\"n\">create<\/span>(<\/span>\n<span class=\"l\">            <span class=\"n\">model<\/span>=<span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"ANTHROPIC_MODEL\"<\/span>, <span class=\"s\">\"claude-haiku-5-5\"<\/span>),<\/span>\n<span class=\"l\">            <span class=\"n\">max_tokens<\/span>=<span class=\"m\">300<\/span>, <span class=\"n\">system<\/span>=<span class=\"n\">SYSTEM<\/span>,<\/span>\n<span class=\"l\">            <span class=\"n\">messages<\/span>=[{<span class=\"s\">\"role\"<\/span>: <span class=\"s\">\"user\"<\/span>, <span class=\"s\">\"content\"<\/span>: <span class=\"n\">prompt<\/span>}],<\/span>\n<span class=\"l\">        )<\/span>\n<span class=\"l\">        <span class=\"n\">text<\/span> = <span class=\"s\">\"\"<\/span>.<span class=\"n\">join<\/span>(<span class=\"n\">b<\/span>.<span class=\"n\">text<\/span> <span class=\"k\">for<\/span> <span class=\"n\">b<\/span> in <span class=\"n\">r<\/span>.<span class=\"n\">content<\/span> <span class=\"k\">if<\/span> <span class=\"n\">b<\/span>.<span class=\"n\">type<\/span> == <span class=\"s\">\"text\"<\/span>)<\/span>\n<span class=\"l\">        <span class=\"k\">return<\/span> {<span class=\"s\">\"text\"<\/span>: <span class=\"n\">text<\/span>,<\/span>\n<span class=\"l\">                <span class=\"s\">\"in\"<\/span>: <span class=\"n\">r<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">input_tokens<\/span>, <span class=\"s\">\"out\"<\/span>: <span class=\"n\">r<\/span>.<span class=\"n\">usage<\/span>.<span class=\"n\">output_tokens<\/span>}<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\">    <span class=\"k\">if<\/span> <span class=\"n\">provider<\/span> == <span class=\"s\">\"gemini\"<\/span>:<\/span>\n<span class=\"l\">        <span class=\"kn\">from<\/span> <span class=\"n\">google<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">genai<\/span><\/span>\n<span class=\"l\">        <span class=\"kn\">from<\/span> <span class=\"n\">google.genai<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">types<\/span><\/span>\n<span class=\"l\">        <span class=\"n\">r<\/span> = <span class=\"n\">genai<\/span>.<span class=\"n\">Client<\/span>().<span class=\"n\">models<\/span>.<span class=\"n\">generate_content<\/span>(<\/span>\n<span class=\"l\">            <span class=\"n\">model<\/span>=<span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"GEMINI_MODEL\"<\/span>, <span class=\"s\">\"gemini-3.8-flash\"<\/span>),<\/span>\n<span class=\"l\">            <span class=\"n\">contents<\/span>=<span class=\"n\">prompt<\/span>,<\/span>\n<span class=\"l\">            <span class=\"n\">config<\/span>=<span class=\"n\">types<\/span>.<span class=\"n\">GenerateContentConfig<\/span>(<\/span>\n<span class=\"l\">                <span class=\"n\">system_instruction<\/span>=<span class=\"n\">SYSTEM<\/span>, <span class=\"n\">max_output_tokens<\/span>=<span class=\"m\">300<\/span>),<\/span>\n<span class=\"l\">        )<\/span>\n<span class=\"l\">        <span class=\"n\">u<\/span> = <span class=\"n\">r<\/span>.<span class=\"n\">usage_metadata<\/span><\/span>\n<span class=\"l\">        <span class=\"k\">return<\/span> {<span class=\"s\">\"text\"<\/span>: <span class=\"n\">r<\/span>.<span class=\"n\">text<\/span>,<\/span>\n<span class=\"l\">                <span class=\"s\">\"in\"<\/span>: <span class=\"n\">u<\/span>.<span class=\"n\">prompt_token_count<\/span>, <span class=\"s\">\"out\"<\/span>: <span class=\"n\">u<\/span>.<span class=\"n\">candidates_token_count<\/span>}<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\">    <span class=\"k\">raise<\/span> <span class=\"n\">ValueError<\/span>(<span class=\"s\">f\"Unknown provider: {<\/span><span class=\"n\">provider<\/span><span class=\"s\">}\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 45, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<div class=\"dd-vsc-pane p2\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a compare.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">time<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">llm<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">ask<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">prompt<\/span> = <span class=\"s\">\"Explain what a REST API is to a beginner.\"<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">for<\/span> <span class=\"n\">provider<\/span> in [<span class=\"s\">\"openai\"<\/span>, <span class=\"s\">\"anthropic\"<\/span>, <span class=\"s\">\"gemini\"<\/span>]:<\/span>\n<span class=\"l\">    <span class=\"n\">start<\/span> = <span class=\"n\">time<\/span>.<span class=\"n\">perf_counter<\/span>()<\/span>\n<span class=\"l\">    <span class=\"n\">result<\/span> = <span class=\"n\">ask<\/span>(<span class=\"n\">provider<\/span>, <span class=\"n\">prompt<\/span>)<\/span>\n<span class=\"l\">    <span class=\"n\">seconds<\/span> = <span class=\"n\">time<\/span>.<span class=\"n\">perf_counter<\/span>() - <span class=\"n\">start<\/span><\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">f\"--- {<\/span><span class=\"n\">provider<\/span><span class=\"s\">} ({<\/span><span class=\"n\">seconds<\/span><span class=\"s\">:.1f}s, {<\/span><span class=\"n\">result<\/span>[<span class=\"s\">'in'<\/span>]<span class=\"s\">} in \/ {<\/span><span class=\"n\">result<\/span>[<span class=\"s\">'out'<\/span>]<span class=\"s\">} out)\"<\/span>)<\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"n\">result<\/span>[<span class=\"s\">\"text\"<\/span>], <span class=\"s\">\"\\n\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Example output \u2013 your text and numbers will differ (answers shortened)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python compare.py\n--- openai (&lt;time&gt;s, &lt;in&gt; in \/ &lt;out&gt; out)\nA REST API lets two programs talk over HTTP using URLs and methods like GET and POST. ...\n\n--- anthropic (&lt;time&gt;s, &lt;in&gt; in \/ &lt;out&gt; out)\nA REST API is a set of web addresses your app can call to read or change data. ...\n\n--- gemini (&lt;time&gt;s, &lt;in&gt; in \/ &lt;out&gt; out)\nThink of a REST API as a waiter: you send a request, it brings back data. ...<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 11, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\"><code>llm.py<\/code> hides the differences; <code>compare.py<\/code> uses it. Open the second tab and press <strong>\u25b6 Run<\/strong>.<\/p>\n\n<p>This is the same idea that frameworks like LangChain and the Vercel AI SDK use, just much smaller. Writing it yourself once helps you understand what those tools do for you (we compare frameworks in Part 18). Notice that the Claude branch joins all text blocks instead of taking only the first one, which is a safer habit.<\/p>\n<\/div>\n\n\n\n<div class=\"dd-post\">\n<h2 id=\"errors\">Handling errors, timeouts and retries<\/h2>\n<p>Network calls fail sometimes. Keys get revoked, you hit a rate limit, or the provider is busy. A good LLM app expects these problems and shows a clear message instead of crashing. The good news: all three official SDKs <strong>automatically retry<\/strong> some temporary errors (like connection problems, 429 and 5xx) a couple of times with a short wait in between. You can tune this with <code>max_retries<\/code> and <code>timeout<\/code>.<\/p>\n\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: safe_call.py\">\n<input type=\"radio\" name=\"v6\" id=\"v6f1\" class=\"f1\" checked>\n<input type=\"checkbox\" id=\"v6r1\" class=\"r1\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">safe_call.py \u2014 devdojo-llm-api \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v6f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>safe_call.py<\/label>\n<label for=\"v6r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a safe_call.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">os<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">import<\/span> <span class=\"n\">sys<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">dotenv<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">load_dotenv<\/span><\/span>\n<span class=\"l\"><span class=\"kn\">from<\/span> <span class=\"n\">openai<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">OpenAI<\/span>, <span class=\"n\">APIConnectionError<\/span>, <span class=\"n\">APIStatusError<\/span>, <span class=\"n\">RateLimitError<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">load_dotenv<\/span>()<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">if<\/span> not <span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"OPENAI_API_KEY\"<\/span>):<\/span>\n<span class=\"l\">    <span class=\"n\">sys<\/span>.<span class=\"n\">exit<\/span>(<span class=\"s\">\"Stop: OPENAI_API_KEY is not set. Add it to your .env file first.\"<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"c\"># The SDK already retries some errors for you - you can tune it:<\/span><\/span>\n<span class=\"l\"><span class=\"n\">client<\/span> = <span class=\"n\">OpenAI<\/span>(<span class=\"n\">max_retries<\/span>=<span class=\"m\">3<\/span>, <span class=\"n\">timeout<\/span>=<span class=\"m\">30.0<\/span>)<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">try<\/span>:<\/span>\n<span class=\"l\">    <span class=\"n\">r<\/span> = <span class=\"n\">client<\/span>.<span class=\"n\">responses<\/span>.<span class=\"n\">create<\/span>(<\/span>\n<span class=\"l\">        <span class=\"n\">model<\/span>=<span class=\"n\">os<\/span>.<span class=\"n\">getenv<\/span>(<span class=\"s\">\"OPENAI_MODEL\"<\/span>, <span class=\"s\">\"gpt-6-luna\"<\/span>),<\/span>\n<span class=\"l\">        <span class=\"nb\">input<\/span>=<span class=\"s\">\"Give me one tip for writing clean code.\"<\/span>,<\/span>\n<span class=\"l\">    )<\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"n\">r<\/span>.<span class=\"n\">output_text<\/span>)<\/span>\n<span class=\"l\"><span class=\"k\">except<\/span> <span class=\"n\">RateLimitError<\/span>:<\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">\"Too many requests (429). Wait a little and try again.\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"k\">except<\/span> <span class=\"n\">APIConnectionError<\/span>:<\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">\"Could not reach the API. Check your internet or proxy.\"<\/span>)<\/span>\n<span class=\"l\"><span class=\"k\">except<\/span> <span class=\"n\">APIStatusError<\/span> <span class=\"k\">as<\/span> <span class=\"n\">e<\/span>:<\/span>\n<span class=\"l\">    <span class=\"c\"># 401 = bad key, 400 = bad request, 404 = wrong model name, 5xx = server problem<\/span><\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">f\"API error {<\/span><span class=\"n\">e<\/span>.<span class=\"n\">status_code<\/span><span class=\"s\">}: {<\/span><span class=\"n\">e<\/span>.<span class=\"n\">message<\/span><span class=\"s\">}\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output \u2013 no key set yet<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python safe_call.py\nStop: OPENAI_API_KEY is not set. Add it to your .env file first.<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 26, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Press <strong>\u25b6 Run<\/strong> to see what happens when the key is missing.<\/p>\n\n<p>The Anthropic SDK has the same error classes (<code>anthropic.RateLimitError<\/code>, <code>anthropic.APIStatusError<\/code> and so on), and the Node.js SDKs throw errors with a <code>status<\/code> property you can check in a <code>try\/catch<\/code>. Checking the key before the call, as shown above, gives beginners a far friendlier message than a long stack trace.<\/p>\n\n<h2 id=\"cost\">Estimating cost from token usage<\/h2>\n<p>In Part 2 you learned that you pay per token, and that output tokens usually cost more than input tokens. Now you have real numbers from <code>usage<\/code>, so you can turn them into money. Prices are listed per <strong>1 million tokens<\/strong> on each provider&#8217;s pricing page. The script below uses example prices only, to show the maths.<\/p>\n\n<div class=\"dd-vsc\" role=\"group\" aria-label=\"Code editor: cost.py\">\n<input type=\"radio\" name=\"v7\" id=\"v7f1\" class=\"f1\" checked>\n<input type=\"checkbox\" id=\"v7r1\" class=\"r1\">\n<div class=\"dd-vsc-bar\"><span class=\"dd-dots\"><i><\/i><i><\/i><i><\/i><\/span><span class=\"dd-vsc-title\">cost.py \u2014 devdojo-llm-api \u2014 Visual Studio Code<\/span><\/div>\n<div class=\"dd-vsc-tabs\">\n<label for=\"v7f1\" class=\"t1\"><b class=\"ic py\">PY<\/b>cost.py<\/label>\n<label for=\"v7r1\" class=\"run run1\" title=\"Show the output in the terminal\">\u25b6 Run<\/label>\n<\/div>\n<div class=\"dd-vsc-pane p1\">\n<div class=\"dd-vsc-crumbs\">devdojo-llm-api \u203a cost.py<\/div>\n<pre class=\"dd-vsc-code\"><code><span class=\"l\"><span class=\"c\"># Estimate the cost of one request from its token usage.<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># Prices are EXAMPLES in US dollars per 1 million tokens.<\/span><\/span>\n<span class=\"l\"><span class=\"c\"># Always check the provider's pricing page for real numbers.<\/span><\/span>\n<span class=\"l\"><span class=\"n\">PRICES<\/span> = {<\/span>\n<span class=\"l\">    <span class=\"s\">\"small model\"<\/span>:  {<span class=\"s\">\"in\"<\/span>: <span class=\"m\">0.25<\/span>, <span class=\"s\">\"out\"<\/span>: <span class=\"m\">2.00<\/span>},<\/span>\n<span class=\"l\">    <span class=\"s\">\"medium model\"<\/span>: {<span class=\"s\">\"in\"<\/span>: <span class=\"m\">3.00<\/span>, <span class=\"s\">\"out\"<\/span>: <span class=\"m\">15.00<\/span>},<\/span>\n<span class=\"l\">}<\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"n\">usage<\/span> = {<span class=\"s\">\"in\"<\/span>: <span class=\"m\">1_200<\/span>, <span class=\"s\">\"out\"<\/span>: <span class=\"m\">350<\/span>}  <span class=\"c\"># copy these from response.usage<\/span><\/span>\n<span class=\"l\"><span class=\"n\">requests_per_day<\/span> = <span class=\"m\">5_000<\/span><\/span>\n<span class=\"l\"><\/span>\n<span class=\"l\"><span class=\"k\">for<\/span> <span class=\"n\">name<\/span>, <span class=\"n\">price<\/span> in <span class=\"n\">PRICES<\/span>.<span class=\"n\">items<\/span>():<\/span>\n<span class=\"l\">    <span class=\"n\">one<\/span> = (<span class=\"n\">usage<\/span>[<span class=\"s\">\"in\"<\/span>] * <span class=\"n\">price<\/span>[<span class=\"s\">\"in\"<\/span>] + <span class=\"n\">usage<\/span>[<span class=\"s\">\"out\"<\/span>] * <span class=\"n\">price<\/span>[<span class=\"s\">\"out\"<\/span>]) \/ <span class=\"m\">1_000_000<\/span><\/span>\n<span class=\"l\">    <span class=\"n\">month<\/span> = <span class=\"n\">one<\/span> * <span class=\"n\">requests_per_day<\/span> * <span class=\"m\">30<\/span><\/span>\n<span class=\"l\">    <span class=\"nb\">print<\/span>(<span class=\"s\">f\"{<\/span><span class=\"n\">name<\/span><span class=\"s\">:&lt;13} ${<\/span><span class=\"n\">one<\/span><span class=\"s\">:.6f} per request  -&gt;  ${<\/span><span class=\"n\">month<\/span><span class=\"s\">:,.2f} per month\"<\/span>)<\/span><\/code><\/pre>\n<div class=\"dd-vsc-term\"><div class=\"dd-term-head\"><span>TERMINAL<\/span><span>OUTPUT<\/span><span>PROBLEMS<\/span><span class=\"dd-term-note\">Real output \u2013 Python 3.13 (example prices)<\/span><\/div><pre><code><span class=\"gp\">$<\/span> python cost.py\nsmall model   $0.001000 per request  -&gt;  $150.00 per month\nmedium model  $0.008850 per request  -&gt;  $1,327.50 per month<\/code><\/pre><\/div>\n<div class=\"dd-vsc-status\"><span>\u2387 main<\/span><span>Ln 15, Col 1<\/span><span class=\"sp\"><\/span><span>Spaces: 4<\/span><span>UTF-8<\/span><span>LF<\/span><span>Python<\/span><\/div>\n<\/div>\n<\/div>\n<p class=\"dd-vsc-hint\">Press <strong>\u25b6 Run<\/strong>, then try your own token numbers.<\/p>\n\n<div class=\"dd-tip\"><strong>A small model can be 10x cheaper.<\/strong> Start every feature with a small, fast model. Move to a bigger model only for the requests where quality really needs it. We will go deeper into this in Part 20 (cost and latency).<\/div>\n\n<h2 id=\"common-errors\">Common errors and fixes<\/h2>\n<div class=\"dd-tablewrap\"><table>\n<tr><th>Error you see<\/th><th>What it means<\/th><th>How to fix it<\/th><\/tr>\n<tr><td><code>401 Unauthorized<\/code> \/ <code>AuthenticationError<\/code><\/td><td>The API key is wrong, deleted or missing.<\/td><td>Check the variable name in <code>.env<\/code>, remove extra spaces or quotes, and create a new key if needed.<\/td><\/tr>\n<tr><td><code>The api_key client option must be set<\/code><\/td><td>The SDK could not find the key in the environment.<\/td><td>Call <code>load_dotenv()<\/code> in Python, or run Node.js with <code>--env-file=.env<\/code>.<\/td><\/tr>\n<tr><td><code>404<\/code> \/ model not found<\/td><td>The model name is misspelled or the model was retired.<\/td><td>Copy the exact model ID from the provider&#8217;s models page and set it in <code>.env<\/code>.<\/td><\/tr>\n<tr><td><code>429 Too Many Requests<\/code><\/td><td>Rate limit hit, or no credit left on the account.<\/td><td>Wait and retry, slow down your loop, or add billing credit.<\/td><\/tr>\n<tr><td><code>400<\/code> with &#8220;max_tokens&#8221;<\/td><td>Claude needs <code>max_tokens<\/code> on every request.<\/td><td>Add <code>max_tokens=...<\/code> to <code>messages.create<\/code>.<\/td><\/tr>\n<tr><td>Answer is cut off mid-sentence<\/td><td>The output token limit was too small.<\/td><td>Increase <code>max_output_tokens<\/code> \/ <code>max_tokens<\/code>; check the stop reason.<\/td><\/tr>\n<tr><td><code>SyntaxError: Cannot use import statement<\/code><\/td><td>Node.js is treating the file as CommonJS.<\/td><td>Use the <code>.mjs<\/code> extension or set <code>\"type\": \"module\"<\/code> in <code>package.json<\/code>.<\/td><\/tr>\n<tr><td><code>ModuleNotFoundError: No module named 'google.genai'<\/code><\/td><td>The new Gemini SDK is not installed (or the old one is).<\/td><td>Run <code>pip install google-genai<\/code> inside your activated virtual environment.<\/td><\/tr>\n<\/table><\/div>\n\n<h2 id=\"best-practices\">Best practices for LLM API calls<\/h2>\n<ul>\n<li><strong>Keep keys on the server.<\/strong> Never call these APIs directly from browser JavaScript or a mobile app with a real key. Put a small backend in between.<\/li>\n<li><strong>Put model names in config.<\/strong> Models change often; an environment variable makes upgrades a one-line change.<\/li>\n<li><strong>Always set an output limit.<\/strong> It protects you from very long (and expensive) answers.<\/li>\n<li><strong>Log the usage numbers.<\/strong> Save input and output tokens for every request so you can track cost per feature or per user.<\/li>\n<li><strong>Set timeouts and handle errors.<\/strong> Show a friendly message, and let the SDK&#8217;s built-in retries handle short hiccups.<\/li>\n<li><strong>Use different keys for development and production<\/strong>, and set spending limits in each provider&#8217;s console.<\/li>\n<li><strong>Do not send private data you do not need.<\/strong> Remove passwords, ID numbers and other secrets from prompts.<\/li>\n<\/ul>\n\n<details>\n<summary>Bonus: what does the raw HTTP request look like?<\/summary>\n<p>Under the hood, the SDK sends a normal HTTPS POST request with a JSON body and your key in a header. For OpenAI it goes to <code>https:\/\/api.openai.com\/v1\/responses<\/code> with an <code>Authorization: Bearer &lt;key&gt;<\/code> header. Claude uses <code>https:\/\/api.anthropic.com\/v1\/messages<\/code> with <code>x-api-key<\/code> and <code>anthropic-version<\/code> headers. Gemini uses <code>generativelanguage.googleapis.com<\/code> with an <code>x-goog-api-key<\/code> header. Knowing this helps when you debug with tools like Postman or curl, but for apps, stick with the SDKs.<\/p>\n<\/details>\n\n<h2 id=\"practice\">Practice task<\/h2>\n<p>Build a tiny command-line &#8220;Ask any model&#8221; tool using the <code>ask()<\/code> helper:<\/p>\n<ol>\n<li>Create <code>cli.py<\/code> (or <code>cli.mjs<\/code>) that reads a provider name and a question from the command line, for example <code>python cli.py gemini \"What is Docker?\"<\/code>.<\/li>\n<li>Print the answer, the input and output tokens, and the time the call took.<\/li>\n<li>Add a <code>--max<\/code> option that sets the output token limit. Try a very small value like 20 and look at the stop reason.<\/li>\n<li>Use <code>cost.py<\/code> logic to print an estimated cost for each call using the real prices from the provider&#8217;s pricing page.<\/li>\n<\/ol>\n<div class=\"dd-tip\"><strong>Bonus:<\/strong> if one provider fails (for example a 429 error), automatically try the next provider in a list. This is called a <em>fallback<\/em>, and many production apps use it.<\/div>\n\n<h2 id=\"faq\">FAQ: LLM API calls in Python and Node.js<\/h2>\n<details><summary>Which LLM API is best for beginners?<\/summary><p>All three are good. Gemini is easy to start with because it has a free tier. OpenAI and Claude need a small prepaid credit but have excellent docs. Learn one well; the others then take minutes.<\/p><\/details>\n<details><summary>Is the ChatGPT Plus subscription the same as API access?<\/summary><p>No. A ChatGPT or Claude.ai subscription is for the chat app only. API usage is billed separately, per token, from the developer platform.<\/p><\/details>\n<details><summary>Can I call an LLM API directly from React or the browser?<\/summary><p>Not with your real key, because anyone could read it in the browser&#8217;s developer tools. Call the API from a backend (Node.js, Python, a serverless function) and let your front end talk to that backend.<\/p><\/details>\n<details><summary>Why does the same prompt give a different answer each time?<\/summary><p>LLMs pick tokens with some randomness. You will learn how to control this with temperature and top-p in Part 5.<\/p><\/details>\n<details><summary>Do I need LangChain to call an LLM?<\/summary><p>No. The official SDKs are enough for most apps. Frameworks can help with bigger pipelines like RAG and agents, which we cover later in the series.<\/p><\/details>\n<details><summary>How do I keep a conversation going with multiple messages?<\/summary><p>Send the earlier messages again with each request (Claude and Gemini accept a list of turns; OpenAI accepts a list of input items or a previous response ID). We build a full chat in Part 7.<\/p><\/details>\n\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>You just made real LLM API calls to OpenAI, Anthropic Claude and Google Gemini from both Python and Node.js. You now know how to keep API keys safe in a <code>.env<\/code> file, how each provider shapes its request and response, where to find the answer text and token usage, how to handle errors and retries, and how to estimate cost. Most importantly, you built a small <code>ask()<\/code> helper that hides the differences, which is the foundation for everything we build next in this series.<\/p>\n<p>If you missed the theory, go back to <a href=\"https:\/\/devdojo.co.in\/?p=219\">Part 2: Tokens, Context Windows and Model Parameters<\/a>. Want to see API calls used inside agents? Read our <a href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/08\/openai-agents-sdk-python\/\">OpenAI Agents SDK tutorial<\/a> and <a href=\"https:\/\/devdojo.co.in\/?p=164\">how to build an MCP server in TypeScript<\/a>.<\/p>\n\n<div class=\"dd-next\"><strong>Next part \u2013 Generative AI Part 4: Prompt Engineering Basics: Roles, Instructions and Examples.<\/strong> Now that you can call a model, we will learn how to talk to it well: system and user roles, clear instructions, few-shot examples and simple prompt patterns that give better answers every time.<\/div>\n\n<div class=\"dd-cta\">\n<h3>New posts every day on DevDojo<\/h3>\n<p>Follow the full <strong>Generative AI series<\/strong> \u2013 30 parts from &#8220;what is an LLM&#8221; to building a production AI assistant. Bookmark <a href=\"https:\/\/devdojo.co.in\/\">devdojo.co.in<\/a> and leave a comment if your first API call gave you any trouble.<\/p>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.<\/p>\n","protected":false},"author":1,"featured_media":222,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[38],"tags":[113,44,43,90,52,112],"class_list":["post-223","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","tag-generative-ai","tag-llm","tag-node-js","tag-openai-api","tag-python","tag-series-generative-ai"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Lokendra\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"DevDojo - Empowering Developers\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"First LLM API Calls in Python &amp; Node.js | GenAI Part 3\" \/>\n\t\t<meta property=\"og:description\" content=\"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-19T14:00:41+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-19T14:00:42+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"First LLM API Calls in Python &amp; Node.js | GenAI Part 3\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#blogposting\",\"name\":\"First LLM API Calls in Python & Node.js | GenAI Part 3\",\"headline\":\"Generative AI Part 3: Your First LLM API Calls in Python and Node.js\",\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/genai-series-part-3-first-llm-api-calls.jpg\",\"width\":1200,\"height\":630,\"caption\":\"Generative AI Part 3 banner: your first LLM API calls in Python and Node.js to OpenAI, Claude and Gemini\"},\"datePublished\":\"2026-10-19T14:00:41+00:00\",\"dateModified\":\"2026-10-19T14:00:42+00:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#webpage\"},\"articleSection\":\"AI, Generative AI, LLM, Node.js, OpenAI API, Python, Series: Generative AI\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/#listItem\",\"name\":\"AI\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/#listItem\",\"position\":2,\"name\":\"AI\",\"item\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#listItem\",\"name\":\"Generative AI Part 3: Your First LLM API Calls in Python and Node.js\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#listItem\",\"position\":3,\"name\":\"Generative AI Part 3: Your First LLM API Calls in Python and Node.js\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/category\\\/ai\\\/#listItem\",\"name\":\"AI\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/\",\"name\":\"Lokendra\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Lokendra\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#webpage\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/\",\"name\":\"First LLM API Calls in Python & Node.js | GenAI Part 3\",\"description\":\"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/author\\\/lokendra\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/genai-series-part-3-first-llm-api-calls.jpg\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#mainImage\",\"width\":1200,\"height\":630,\"caption\":\"Generative AI Part 3 banner: your first LLM API calls in Python and Node.js to OpenAI, Claude and Gemini\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/index.php\\\/2026\\\/10\\\/19\\\/genai-series-part-3-first-llm-api-calls\\\/#mainImage\"},\"datePublished\":\"2026-10-19T14:00:41+00:00\",\"dateModified\":\"2026-10-19T14:00:42+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#website\",\"url\":\"https:\\\/\\\/devdojo.co.in\\\/\",\"name\":\"DevDojo\",\"description\":\"Empowering Developers\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/devdojo.co.in\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"First LLM API Calls in Python & Node.js | GenAI Part 3","description":"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.","canonical_url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#blogposting","name":"First LLM API Calls in Python & Node.js | GenAI Part 3","headline":"Generative AI Part 3: Your First LLM API Calls in Python and Node.js","author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/10\/genai-series-part-3-first-llm-api-calls.jpg","width":1200,"height":630,"caption":"Generative AI Part 3 banner: your first LLM API calls in Python and Node.js to OpenAI, Claude and Gemini"},"datePublished":"2026-10-19T14:00:41+00:00","dateModified":"2026-10-19T14:00:42+00:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#webpage"},"isPartOf":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#webpage"},"articleSection":"AI, Generative AI, LLM, Node.js, OpenAI API, Python, Series: Generative AI"},{"@type":"BreadcrumbList","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","position":1,"name":"Home","item":"https:\/\/devdojo.co.in\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/#listItem","name":"AI"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/#listItem","position":2,"name":"AI","item":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/","nextItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#listItem","name":"Generative AI Part 3: Your First LLM API Calls in Python and Node.js"},"previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#listItem","position":3,"name":"Generative AI Part 3: Your First LLM API Calls in Python and Node.js","previousItem":{"@type":"ListItem","@id":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/#listItem","name":"AI"}}]},{"@type":"Organization","@id":"https:\/\/devdojo.co.in\/#organization","name":"DevDojo","description":"Empowering Developers","url":"https:\/\/devdojo.co.in\/"},{"@type":"Person","@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author","url":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/","name":"Lokendra","image":{"@type":"ImageObject","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/99f102f0cc9080237ce54b91304fd1b3b6fa29925cbaeba93cd796872e58b45c?s=96&d=mm&r=g","width":96,"height":96,"caption":"Lokendra"}},{"@type":"WebPage","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#webpage","url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/","name":"First LLM API Calls in Python & Node.js | GenAI Part 3","description":"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/devdojo.co.in\/#website"},"breadcrumb":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#breadcrumblist"},"author":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"creator":{"@id":"https:\/\/devdojo.co.in\/index.php\/author\/lokendra\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/devdojo.co.in\/wp-content\/uploads\/2026\/10\/genai-series-part-3-first-llm-api-calls.jpg","@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#mainImage","width":1200,"height":630,"caption":"Generative AI Part 3 banner: your first LLM API calls in Python and Node.js to OpenAI, Claude and Gemini"},"primaryImageOfPage":{"@id":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/#mainImage"},"datePublished":"2026-10-19T14:00:41+00:00","dateModified":"2026-10-19T14:00:42+00:00"},{"@type":"WebSite","@id":"https:\/\/devdojo.co.in\/#website","url":"https:\/\/devdojo.co.in\/","name":"DevDojo","description":"Empowering Developers","inLanguage":"en-US","publisher":{"@id":"https:\/\/devdojo.co.in\/#organization"}}]},"og:locale":"en_US","og:site_name":"DevDojo - Empowering Developers","og:type":"article","og:title":"First LLM API Calls in Python &amp; Node.js | GenAI Part 3","og:description":"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.","og:url":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/","article:published_time":"2026-10-19T14:00:41+00:00","article:modified_time":"2026-10-19T14:00:42+00:00","twitter:card":"summary_large_image","twitter:title":"First LLM API Calls in Python &amp; Node.js | GenAI Part 3","twitter:description":"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step."},"aioseo_meta_data":{"post_id":"223","title":"First LLM API Calls in Python & Node.js | GenAI Part 3","description":"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.","keywords":null,"keyphrases":{"focus":{"keyphrase":"LLM API calls"},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":"First LLM API Calls in Python & Node.js | GenAI Part 3","og_description":"Make your first LLM API calls in Python and Node.js with OpenAI, Claude and Gemini. Learn API keys, requests, token usage, errors and cost, step by step.","og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"BlogPosting","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":0,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-10-19 13:05:20","updated":"2026-10-19 14:02:55","focus_keyword":"LLM API calls","additional_keywords":null,"truseo_locale":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/devdojo.co.in\/index.php\/category\/ai\/\" title=\"AI\">AI<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tGenerative AI Part 3: Your First LLM API Calls in Python and Node.js\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/devdojo.co.in\/"},{"label":"AI","link":"https:\/\/devdojo.co.in\/index.php\/category\/ai\/"},{"label":"Generative AI Part 3: Your First LLM API Calls in Python and Node.js","link":"https:\/\/devdojo.co.in\/index.php\/2026\/10\/19\/genai-series-part-3-first-llm-api-calls\/"}],"_links":{"self":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/223","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/comments?post=223"}],"version-history":[{"count":1,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/223\/revisions"}],"predecessor-version":[{"id":224,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/posts\/223\/revisions\/224"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media\/222"}],"wp:attachment":[{"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/media?parent=223"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/categories?post=223"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devdojo.co.in\/index.php\/wp-json\/wp\/v2\/tags?post=223"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}