{"course":{"slug":"choosing-your-tools","title":"Choosing and Using the Tools","url":"https://addaly.com/learn/choosing-your-tools","level":"beginner","track":"work"},"lesson":{"slug":"keys-and-spend-caps","title":"Keys, spend caps, and the bill nobody wants","url":"https://addaly.com/learn/choosing-your-tools/keys-and-spend-caps","position":22,"of":76,"minutes":8,"answer":"Set a hard monthly spend cap before your first API request, keep keys out of any file you might share, use one key per use so a leak revokes one thing.","keyIdea":"Set a hard monthly spend cap before your first API request, keep keys out of any file you might share, use one key per use so a leak revokes one thing, and never put a key anywhere a user's device can read it."},"body":{"format":"markdown","text":"## The failure that hurts\n\nA student pushes a small project to a public code repository. The API key is in a configuration file. Automated scanners find keys on public repositories within minutes (this is a well-documented, industrialised activity) and someone else's job now runs on that account. The first the student knows is the invoice.\n\nThis is common enough that the major providers scan public code themselves and revoke keys they find. Do not rely on being rescued.\n\n## Five rules, in order of importance\n\n**1. Set a hard spend limit on day one.** Every provider console has a billing page with a maximum monthly spend and email alerts at thresholds. Set the maximum to something you could pay without difficulty: for a person learning, $5 or $10. This is the single most important step and it takes ninety seconds. Do it before your first request, not after your first surprise.\n\n**2. Never put a key in a file you might share.** Keys live in environment variables or in a `.env` file that is listed in `.gitignore`.\n\n```bash\nexport OPENAI_API_KEY=\"sk-...\"          # in ~/.zshrc or ~/.bashrc\necho \".env\" >> .gitignore               # before the first commit, not after\n```\n\nNote the ordering. Adding `.env` to `.gitignore` after you have committed it does nothing; the key is already in the history, and deleting the file in a later commit does not remove it from the repository.\n\n**3. One key per use.** A key for your laptop, a key for the small tool you wrote, a key for the front-end app. When one leaks, you revoke one key and everything else keeps working. A single shared key means a leak takes down everything you have.\n\n**4. Revoke rather than investigate.** If you think a key may have leaked, delete it in the console and make a new one. It takes twenty seconds and costs nothing. There is no situation where waiting to be sure is the better move.\n\n**5. Check the bill in the first week.** Usage dashboards show spend by day and by model. Look on day three. The gap between what people expect and what they spend is largest at the start, and it is almost always a loop that ran more times than intended.\n\n## Where the money actually goes on an API\n\nThree patterns account for nearly every unexpected bill:\n\n**Resending the conversation.** In a chat, every turn resends everything before it. A long conversation's twentieth message costs many times its first. Most front-ends let you limit how many previous messages are sent; a window of the last ten is usually plenty.\n\n**A loop with no limit.** Something that retries on failure, or processes a list that is longer than you thought. Always test on ten items before running on ten thousand, and print the running cost.\n\n**Thinking mode left on.** Hidden reasoning tokens are billed at the output rate. A per-request cost can be ten times what the visible answer suggests.\n\n## Sharing a key with other people\n\nDo not. If a colleague needs access, they create their own account, or you use a provider or router that issues separate keys with separate limits per person. A shared key means shared spending, no way to tell whose request cost what, and no way to cut off one person.\n\nThe same applies to a key inside anything a user can reach. A key in a mobile app or in a web page's JavaScript is a public key, no matter how it is obfuscated, because anyone can read what their own device received. Requests from a browser or an app have to go through a small server of yours that holds the key.\n\n> Figure: What one long conversation costs, turn by turn. Bars (US cents for that single turn): Turn 1 0.3; Turn 5 1.2; Turn 10 2.4; Turn 20 4.7. Priced at three dollars per million input tokens, with roughly seven hundred tokens added per exchange. Nothing about turn twenty is harder than turn one; everything before it is going up the wire again. Capping the history at the last ten messages is the highest-value setting in any front-end.\n\n## The honest comparison with a subscription\n\nA subscription has one genuine advantage that gets overlooked: it cannot surprise you. The price is the price, and a runaway loop just gets you rate-limited rather than billed. For somebody on a tight budget who is not confident about what they are running, that predictability is worth real money.\n\nUse the API when you want control and low cost per use, and set the cap so that the worst case is a bad afternoon rather than a bad month."},"check":{"question":"A key committed to a public repository is deleted in a later commit. Why is the key still unsafe?","options":[{"text":"Because provider scanners have already flagged the key and it must be re-issued through support before it can be trusted again.","correct":false,"tempting":"Describes a rescue mechanism rather than the problem; scanning is inconsistent and the exposure exists whether or not anyone flagged it."},{"text":"Because the repository's cached copies on other machines will keep the old file until every clone is refreshed.","correct":false,"tempting":"Points at stale clones, a side issue next to the fact that the secret sits permanently in the public history."},{"text":"Because keys cannot be revoked once used, so the only remedy is closing the billing account entirely.","correct":false,"tempting":"Claims an irreversibility that does not exist; revoking a key in the console is immediate and is exactly the right response."},{"text":"Because deleting a file in a new commit leaves the earlier commit, and its contents, in the repository history.","correct":true}]},"prev":{"slug":"first-api-call","title":"Your first API call","url":"https://addaly.com/learn/choosing-your-tools/first-api-call"},"next":{"slug":"free-front-ends","title":"Building your own chat app for nothing","url":"https://addaly.com/learn/choosing-your-tools/free-front-ends"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Keys, spend caps, and the bill nobody wants', in Choosing and Using the Tools. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/choosing-your-tools/keys-and-spend-caps (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Keys, spend caps, and the bill nobody wants","courseTitle":"Choosing and Using the Tools","module":{"index":3,"slug":"the-routes-in","title":"The routes in"},"answer":null,"keyIdea":"Set a hard monthly spend cap before your first API request, keep keys out of any file you might share, use one key per use so a leak revokes one thing, and never put a key anywhere a user’s device can read it.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"bars","unit":"US cents for that single turn","title":"What one long conversation costs, turn by turn","series":[{"label":"Turn 1","value":0.3},{"label":"Turn 5","value":1.2},{"label":"Turn 10","value":2.4},{"label":"Turn 20","value":4.7,"highlight":true}],"caption":"Priced at three dollars per million input tokens, with roughly seven hundred tokens added per exchange. Nothing about turn twenty is harder than turn one; everything before it is going up the wire again. Capping the history at the last ten messages is the highest-value setting in any front-end."},"alt":"Figure: What one long conversation costs, turn by turn. Bars (US cents for that single turn): Turn 1 0.3; Turn 5 1.2; Turn 10 2.4; Turn 20 4.7. Priced at three dollars per million input tokens, with roughly seven hundred tokens added per exchange. Nothing about turn twenty is harder than turn one; everything before it is going up the wire again. Capping the history at the last ten messages is the highest-value setting in any front-end."}},"blocks":[{"t":"h2","id":"the-failure-that-hurts","runs":[{"text":"The failure that hurts"}]},{"t":"p","runs":[{"text":"A student pushes a small project to a public code repository. The API key is in a configuration file. Automated scanners find keys on public repositories within minutes (this is a well-documented, industrialised activity) and someone else’s job now runs on that account. The first the student knows is the invoice."}]},{"t":"p","runs":[{"text":"This is common enough that the major providers scan public code themselves and revoke keys they find. Do not rely on being rescued."}]},{"t":"h2","id":"five-rules-in-order-of-importance","runs":[{"text":"Five rules, in order of importance"}]},{"t":"p","runs":[{"text":"1. Set a hard spend limit on day one.","b":true},{"text":" Every provider console has a billing page with a maximum monthly spend and email alerts at thresholds. Set the maximum to something you could pay without difficulty: for a person learning, $5 or $10. This is the single most important step and it takes ninety seconds. Do it before your first request, not after your first surprise."}]},{"t":"p","runs":[{"text":"2. Never put a key in a file you might share.","b":true},{"text":" Keys live in environment variables or in a "},{"text":".env","code":true},{"text":" file that is listed in "},{"text":".gitignore","code":true},{"text":"."}]},{"t":"code","lang":"bash","label":"Bash","text":"export OPENAI_API_KEY=\"sk-...\"          # in ~/.zshrc or ~/.bashrc\necho \".env\" >> .gitignore               # before the first commit, not after"},{"t":"p","runs":[{"text":"Note the ordering. Adding "},{"text":".env","code":true},{"text":" to "},{"text":".gitignore","code":true},{"text":" after you have committed it does nothing; the key is already in the history, and deleting the file in a later commit does not remove it from the repository."}]},{"t":"p","runs":[{"text":"3. One key per use.","b":true},{"text":" A key for your laptop, a key for the small tool you wrote, a key for the front-end app. When one leaks, you revoke one key and everything else keeps working. A single shared key means a leak takes down everything you have."}]},{"t":"p","runs":[{"text":"4. Revoke rather than investigate.","b":true},{"text":" If you think a key may have leaked, delete it in the console and make a new one. It takes twenty seconds and costs nothing. There is no situation where waiting to be sure is the better move."}]},{"t":"p","runs":[{"text":"5. Check the bill in the first week.","b":true},{"text":" Usage dashboards show spend by day and by model. Look on day three. The gap between what people expect and what they spend is largest at the start, and it is almost always a loop that ran more times than intended."}]},{"t":"h2","id":"where-the-money-actually-goes-on-an-api","runs":[{"text":"Where the money actually goes on an API"}]},{"t":"p","runs":[{"text":"Three patterns account for nearly every unexpected bill:"}]},{"t":"p","runs":[{"text":"Resending the conversation.","b":true},{"text":" In a chat, every turn resends everything before it. A long conversation’s twentieth message costs many times its first. Most front-ends let you limit how many previous messages are sent; a window of the last ten is usually plenty."}]},{"t":"p","runs":[{"text":"A loop with no limit.","b":true},{"text":" Something that retries on failure, or processes a list that is longer than you thought. Always test on ten items before running on ten thousand, and print the running cost."}]},{"t":"p","runs":[{"text":"Thinking mode left on.","b":true},{"text":" Hidden reasoning tokens are billed at the output rate. A per-request cost can be ten times what the visible answer suggests."}]},{"t":"h2","id":"sharing-a-key-with-other-people","runs":[{"text":"Sharing a key with other people"}]},{"t":"p","runs":[{"text":"Do not. If a colleague needs access, they create their own account, or you use a provider or router that issues separate keys with separate limits per person. A shared key means shared spending, no way to tell whose request cost what, and no way to cut off one person."}]},{"t":"p","runs":[{"text":"The same applies to a key inside anything a user can reach. A key in a mobile app or in a web page’s JavaScript is a public key, no matter how it is obfuscated, because anyone can read what their own device received. Requests from a browser or an app have to go through a small server of yours that holds the key."}]},{"t":"h2","id":"the-honest-comparison-with-a-subscription","runs":[{"text":"The honest comparison with a subscription"}]},{"t":"p","runs":[{"text":"A subscription has one genuine advantage that gets overlooked: it cannot surprise you. The price is the price, and a runaway loop just gets you rate-limited rather than billed. For somebody on a tight budget who is not confident about what they are running, that predictability is worth real money."}]},{"t":"p","runs":[{"text":"Use the API when you want control and low cost per use, and set the cap so that the worst case is a bad afternoon rather than a bad month."}]}],"check":{"question":[{"text":"A key committed to a public repository is deleted in a later commit. Why is the key still unsafe?"}],"options":[{"text":[{"text":"Because provider scanners have already flagged the key and it must be re-issued through support before it can be trusted again."}],"correct":false,"tempting":[{"text":"Describes a rescue mechanism rather than the problem; scanning is inconsistent and the exposure exists whether or not anyone flagged it."}]},{"text":[{"text":"Because the repository’s cached copies on other machines will keep the old file until every clone is refreshed."}],"correct":false,"tempting":[{"text":"Points at stale clones, a side issue next to the fact that the secret sits permanently in the public history."}]},{"text":[{"text":"Because keys cannot be revoked once used, so the only remedy is closing the billing account entirely."}],"correct":false,"tempting":[{"text":"Claims an irreversibility that does not exist; revoking a key in the console is immediate and is exactly the right response."}]},{"text":[{"text":"Because deleting a file in a new commit leaves the earlier commit, and its contents, in the repository history."}],"correct":true,"tempting":null}],"order":[1,0,3,2],"why":null},"prev":{"slug":"first-api-call","title":"Your first API call","minutes":9},"next":{"slug":"free-front-ends","title":"Building your own chat app for nothing","minutes":8}}}