מדריך Claude API: בניית אפליקציית Python ב-VPS
למדו איך להקים כלי Python ב-Ubuntu 24.04 עם Claude API, כולל שימוש ב-streaming, טיפול בשגיאות מבוסס טיפוסים ובקרת עלויות מדויקת בשרת ה-VPS שלכם.
מה אתם בונים
כלי בשורת פקודה (command-line tool) על שרת VPS חדש עם Ubuntu 24.04. תזינו לכלי הודעת שגיאה או קטע מתוך log, ותקבלו אבחנה בשפה פשוטה: journalctl -u nginx -n 50 | explain. הפרויקט מורכב מסי fichת Python של כשישים שורות, והוא כולל את כל המרכיבים הנדרשים לאפליקציית Claude API אמיתית: מפתח (key) מאוחסן בצורה נכונה, סביבה וירטואלית (virtualenv), מבני התגובה של ה-SDK, streaming, שרשרת חריגות (exception chain) מוגדרת טיפוסים (typed), ויחידת systemd כדי שהכלי ירוץ באופן עצמאי.
בחרתי בפרויקט הזה בכוונה. רוב המדריכים ל"אפליקציית API ראשונה" גורמים לכם לבנות chatbot שלא תפתחו שוב לעולם. כלי להסברת לוגים (log explainer) הוא כלי שימושי בשרת כבר מהיום הראשון, והוא מחייב אתכם להתמודד עם שני הדברים שמתחילים טועים בהם בפועל: קריאה נכונה של אובייקט התגובה, ובקרת עלויות. ה-API מחייב לפי token ללא תקרה, מלבד אלו שתגדירו בעצמכם; לכן, בקרת עלויות היא חלק בלתי נפרד מהתכנון ולא מחשבה מאוחרת — אותה משמעת נדרשת גם כאשר תעברו ל-running Claude Code on this same VPS in tmux.
קבלת מפתח API מה-Console
ניהול הגישה ל-API מתבצע ב-Anthropic Console בכתובת platform.claude.com — יש להירשם, ולאחר מכן ליצור מפתח תחת Settings → API Keys (קישור התיעוד מפנה ישירות ל-platform.claude.com/settings/keys). המפתח מוצג פעם אחת בלבד, הוא מתחיל ב-sk-ant-%, ולא ניתן לשחזר אותו לאחר מכן — יש להעתיק אותו מיד או למחוק ולהנפיק מחדש.
לגבי עלויות: החל מיולי 2026, אין מסלול חינמי מתמשך עבור ה-API. לפי תיעוד התמחור של Anthropic, משתמשים חדשים מקבלים סכום קטן של קרדיטים בחינם לצורך בדיקות; הסכום המדויק הוא מה שמוצג ב-Console בעת ההרשמה. לאחר שהקרדיט ينגמר, יש להטעין את החשבון כדי שהבקשות יצליחו. זהו נושא נפרד ממנוי ב-claude.ai — תוכנית Pro או Max אינה כוללת קרדיט ל-API, ומפתח API אינו מעניק גישה לאפליקציית הצ'אט. אם אתם שוקלים בין מנוי לבין API, נושא זה נדון בנפרד: איזה תוכנית Claude אתם באמת צריכים.
צרו את המפתח עם הרשאות (scope) המוגבלות לפרויקט או לשרת אחד בלבד. כאשר מפתח דולף — ועל ציר זמן ארוך מספיק, מפתח כזה ידלוף — תרצו לבטל אותו מבלי להשבית שירותים אחרים בבעלותכם.
Keep the key out of .bashrc
השימוש ב-reflexive move הוא export ANTHROPIC_API_KEY=sk-ant-... ב-~/.bashrc. אל תעשה זאת. ישנן שלוש בעיות נפרדות:
- כל תהליך יורש אותו. משתנה סביבה שעבר export ב-login shell שלך מועבר לכל מה שתפעיל — לאפליקציית ה-web, ל-crash reporter שמצרף את משתני הסביבה שלו לדו"ח תקלה, ולדף ה-
phpinfo()שמישהו השאיר פתוח. מרחב החשיפה של המפתח הופך ל"כל מה שהמשתמש מריץ". - הקלדה שלו שומרת אותו ב-
~/.bash_history. אם תריץ את ה-export ידנית פעם אחת, המפתח יישמר בקובץ plaintext לנצח, ויסונכרן לכל גיבוי של ה-home directory שלך. - הוא לא יהיה זמין כש-systemd יזדקק לו. שירותים (services) אינם קוראים את ה-
.bashrcשלך, ולכן התבנית נכשלת בדיוק ברגע שתהפוך את הסקריפט ל-unit — בדרך כלל כ-401 מסתורי בשעה 6:00 בבוקר.
התבנית הנכונה בשרת היא קובץ סביבה ייעודי עם הרשאות 600, הטענה שלו מתבצעת אך ורק על ידי התהליך שזקוק לו:
sudo mkdir -p /opt/explain
sudo install -m 600 -o root -g root /dev/null /etc/claude-explain.env
printf 'ANTHROPIC_API_KEY=sk-ant-YOUR-KEY-HERE\n' | sudo tee /etc/claude-explain.env >/dev/nullהשתמש ב-tee מתוך printf במקום בעורך טקסט אם ברצונך למנוע מהמפתח להופיע בקובצי swap של עורך; בכל מקרה, ודא באמצעות ls -l /etc/claude-explain.env שהקובץ קורא את -rw------- וש-root הוא הבעלים שלו. shells אינטראקטיביים מקבלים את המפתח בכל הרצה דרך wrapper (להלן), ו-systemd מקבל אותו דרך EnvironmentFile= — ה-root קורא את הקובץ לפני הורדת ההרשאות, כך שלמשתמש השירות (service user) אין צורך בהרשאת קריאה אליו. המפתח לעולם לא יופיע בקוד, ב-git, בפלט של ps, או ב-shell history.
התקנת ה-SDK בתוך venv
בגרסת Ubuntu 24.04 מגיע Python 3.12 עם אכיפת PEP 668. לכן, הרצת pip install anthropic ישירות מול המפרש של המערכת תיכשל עם error: externally-managed-environment. שגיאה זו מעידה על פעולה תקינה של מערכת ההפעלה — יש להשתמש ב-virtualenv:
sudo apt update && sudo apt install -y python3-venv
sudo python3 -m venv /opt/explain/venv
sudo /opt/explain/venv/bin/pip install anthropicאין צורך בתהליך activation בשרת: הפעלת /opt/explain/venv/bin/python ישירות תשתמש תמיד בחבילות של ה-venv.
קריאה ראשונה, וקריאת התגובה בצורה נכונה
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[{"role": "user", "content": "Explain what a systemd unit file is in three sentences."}],
)
for block in response.content:
if block.type == "text":
print(block.text)שני דברים בשתים-עשר השורות הללו מהווים את עיקר המודל המחשבתי של ה-API. ראשון, anthropic.Anthropic() ללא ארгуמנטים קורא את המפתח מה-environment — לעולם אל תעביר אותו כ-string literal. שנית, response.content הוא list of content blocks, ולא string. אם תדפיס אותו ישירות, תקבל את הפלט הקלאסי של משתמשים מתחילים:
[TextBlock(citations=None, text='A systemd unit file is...', type='text')]זה אינו bug; זהו ה-repr של האובייקט. תגובות יכולות להכיל סוגי בלוקים מרובים (text, tool calls, thinking), לכן יש לבצע iteration ולבדוק את block.type == "text" לפני הגישה ל-.text. הטמע את הלולאה הזו כבר ביום הראשון, ותמנע קטגוריה שלמה של בלבול מסוג "הוא מדפיס זבל".
השתמש ב-model ID המדויק claude-opus-4-8. מזהי הדור הנוכחי אינם כוללים תאריך — אל תתרגל (או אל תסמוך על פוסטים ישנים) להוסיף סיומת תאריך; פעולה זו תגרום לשגיאת 404, שתפורט בהמשך.
הכלי בפועל: הסבר
להלן התוכנית המלאה — קלט מ-stdin, פלט אבחון ב-stream, וטיפול בשגיאות:
#!/usr/bin/env python3
"""explain: pipe an error or log excerpt in, get a diagnosis out."""
import sys
import anthropic
MODEL = "claude-opus-4-8"
def main() -> int:
text = sys.stdin.read().strip()
if not text:
print("usage: journalctl -u nginx -n 50 | explain", file=sys.stderr)
return 1
client = anthropic.Anthropic()
try:
with client.messages.stream(
model=MODEL,
max_tokens=1500,
system=(
"You are a senior Linux sysadmin. The user pipes you server "
"logs or error output. Name the most likely cause outright, "
"then give the commands to confirm and fix it. Be terse."
),
messages=[{"role": "user", "content": text}],
) as stream:
for chunk in stream.text_stream:
print(chunk, end="", flush=True)
print()
except anthropic.RateLimitError as e:
retry_after = e.response.headers.get("retry-after", "60")
print(f"rate limited; retry in {retry_after}s", file=sys.stderr)
return 2
except anthropic.APIStatusError as e:
print(f"API error {e.status_code}: {e.message}", file=sys.stderr)
return 2
except anthropic.APIConnectionError:
print("network error reaching the API", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
sys.exit(main())שמרו את הקובץ בשם /opt/explain/explain.py, ולאחר מכן הוסיפו wrapper הטוען את המפתח לשימוש אינטראקטיבי:
sudo tee /usr/local/bin/explain >/dev/null <<'EOF'
#!/bin/sh
set -a; . /etc/claude-explain.env; set +a
exec /opt/explain/venv/bin/python /opt/explain/explain.py "$@"
EOF
sudo chmod 755 /usr/local/bin/explain(ה-wrapper חייב לרוץ באמצעות sudo, או שקובץ ה-env חייב להיות שייך לקבוצה שאליה משתמש ה-admin שלכם שייך — בחרו אפשרות אחת במכוון במקום לשנות את הרשאות הקובץ ל-644.)
מדוע להשתמש ב-streaming. client.messages.stream מדפיס tokens ברגע הגעתם במקום להמתין לסיום כל תהליך היצירה, וזה מונע HTTP timeouts בפלטים ארוכים — ה-SDK יסרב בפועל לקבל ערכי max_tokens גדולים מאוד בקריאות ללא streaming בדיוק מהסיבה הזו. אם אתם זקוקים לאובייקט המלא לאחר מכן, השתמשו ב-stream.get_final_message() בתוך בלוק ה-with.
מדוע סדר החריגות הזה. ה-SDK מעלה חריגות (exceptions) מוגדרות, מהספציפיות ביותר: RateLimitError היא שגיאת 429 וכוללת header מסוג retry-after המציין כמה זמן יש להמתין; APIStatusError מכסה תגובות אחרות שאינן 2xx (בדקו את e.status_code >= 500 לתקלות בצד השרת); APIConnectionError פירושו שהבקשה לא קיבלה תגובה כלל. ולפני שתבנו לולאת ניסוי חוזר (retry loop): ה-SDK כבר מבצע ניסוי חוזר עבור שגיאות 429 ו-5xx בעצמו, פעמיים כברירת מחדל עם exponential backoff (max_retries בצד הלקוח). עד שה-except שלכם ירוץ, ניסוי החזרות כבר יתוקנו — לכן הפעולה הנכונה ב-CLI היא לדווח ולצאת, ולא להמתין ולשלוח בקשות נוספות.
Cost control
סעיף זה נחוץ מכיוון של-API אין תקרה חודשית מובנית מעבר למה שתגדירו, וכל טעות כאן תצטבר בשקט.
max_tokens הוא תקרת ההוצאה לכל קריאה. טוקנים של output הם היקרים יותר — ב-Opus 4.8, המחיר גבוה פי חמישה ממחיר ה-input — ו-max_tokens הוא תקרה קשיחה לכמות שהמודל רשאי לייצר. Prompt שרץ ללא שליטה לא יעלה יותר ב-output ממה שהגדרתם. התאימו את הגודל למשימה: 1,500 טוקנים מספיקים לאבחון log; משימת classification דורשת 100. אם התשובות נקטעות באמצע משפט עם stop_reason: "max_tokens", הגדרתם תקרה נמוכה מדי — הגדילו אותה במודע במקום להשתמש בברירת מחדל גדולה מדי.
ספרו לפני השליחה. גם ה-input עולה כסף, ו-logs הם נפחיים. ל-API יש endpoint לספירה שהוא בחינם (יש לו מגבלות rate limits משלו, נפרדות מיצירת הודעות):
count = client.messages.count_tokens(
model="claude-opus-4-8",
messages=[{"role": "user", "content": big_log_text}],
)
print(count.input_tokens)השתמשו בו כדי למנוע מצב שבו log בנפח 2 GB נשלח לכלי בטעות. אל תשתמשו ב-tiktoken למטרה זו — זהו ה-tokenizer של OpenAI, והוא סופר פחות בכ-15–20% מהטוקנים של Claude בטקסט רגיל, ויותר מכך בקוד.
בחרו את המודל לפי המשימה, לא לפי נאמנות. נכון ליולי 2026, Opus 4.8 (claude-opus-4-8) עולה $5 למיליון טוקנים של input ו-$25 למיליון טוקנים של output; Haiku 4.5 (claude-haiku-4-5) עולה $1/$5 עם context של 200K; Sonnet 5 (claude-sonnet-5) נמצא באמצע בעלות של $3/$15, עם מחיר מבוא של $2/$10 עד ה-31 באוגוסט 2026. באופן קונקרטי: קטע log של 2,000 טוקנים עם תשובה של 500 טוקנים עולה כ-$0.0225 ב-Opus וכ-$0.0045 ב-Haiku. התחילו ב-Opus בזמן שאתם בוחנים את איכות הפלט, ולאחר מכן נסו את אותם prompts ב-Haiku — עבור טרנספורמציות פשוטות בנפח גבוה, התוצאה לרוב בלתי ניתנת להבחנה בחמישית מהמחיר. ודאו את המספרים העדכניים בדף המחירים לפני שתקבעו אותם בתוך תקציב.
Batches לכל מה שיכול לחכות. ה-Batches API מעבד בקשות באופן אסינכרוני ב-50% מהמחיר הסטנדרטי, ורוב ה-batches מסתיימים תוך שעה. סיכומי לילה, מילוי נתונים (backfills), classification בכמות גדולה — כל דבר שאין בו אדם שמחכה לו, שייך לשם.
Prompt caching ל-context חוזר. אם כל קריאה שולחת מחדש את אותו system prompt גדול או runbook, סמנו אותו כניתן ל-cache:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
system=[{
"type": "text",
"text": RUNBOOK_TEXT, # the same 30K tokens on every call
"cache_control": {"type": "ephemeral"},
}],
messages=[{"role": "user", "content": question}],
)
print(response.usage.cache_read_input_tokens) # non-zero from the second call onכתיבת cache עולה בערך 1.25x ממחיר ה-input, קריאת cache עולה בערך 0.1x, עם TTL של 5 דקות — כך שהקריאה השנייה בתוך החלון הזמן כבר משלמת על הראשונה. שתי הערות. ה-prefix שנשמר ב-cache חייב לעבור מינימום המוגדר לכל מודל — כמה אלפי טוקנים ב-Opus — כך ש-system prompt קצר לא יישמר ב-cache כלל. ואם cache_read_input_tokens נשאר על אפס לאורך קריאות זהות, משהו ב-prefix שלכם משתנה בכל בקשה (timestamp הוא הגורם הנפוץ).
זכרו מה נחשב כ-input. system prompts, הגדרות כלים (tool definitions), ובשיחות רב-שלביות (multi-turn) — כל ההיסטוריה שאתם שולחים מחדש בכל שלב נחשבת כטוקנים של input. לולאת צ'אט שמעולם לא מצמצמת את ההיסטוריה תתקע בעלויות גדלות באופן ריבוע; כדאי להבין את החישוב המלא לפני שתבנו כל דבר שיConversational: איך השימוש בטוקנים ובחיוב של Claude עובד בפועל.
הרצה תחת systemd
התועלת משימוש בקובץ-סביבה (environment-file): timer שמסכם את שגיאות האתמול בכל בוקר.
# /etc/systemd/system/log-digest.service
[Unit]
Description=Daily error-log digest via the Claude API
[Service]
Type=oneshot
User=explain
Group=systemd-journal
EnvironmentFile=/etc/claude-explain.env
ExecStart=/bin/sh -c 'journalctl -p err --since yesterday | /opt/explain/venv/bin/python /opt/explain/explain.py >> /var/log/log-digest.txt'# /etc/systemd/system/log-digest.timer
[Unit]
Description=Run the log digest every morning
[Timer]
OnCalendar=06:15
Persistent=true
[Install]
WantedBy=timers.targetsudo useradd -r -s /usr/sbin/nologin explain
sudo touch /var/log/log-digest.txt && sudo chown explain /var/log/log-digest.txt
sudo systemctl daemon-reload
sudo systemctl enable --now log-digest.timer
sudo systemctl start log-digest.service # test it once, right nowשים לב ליתרון של EnvironmentFile=: ה-systemd קורא את הקובץ (בבעלות root ו-mode-600) לפני המעבר למשתמש הלא-מprivileged explain, כך שהתהליך מקבל את המשתנה בעוד המשתמש אינו יכול לקרוא את קובץ המפתח. הקבוצה systemd-journal מעניקה גישה ללוגים. בדוק באמצעות systemctl start ידני וקרא את journalctl -u log-digest.service — אל תחכה לשעה 06:15 כדי לגלות שגיאת כתיב. כאשר דפוס זה הופך למורכב מדי עבור shell pipeline, אותו גישה של מפתח-בתוך-environment-file תקפה גם עבור workflows של n8n מבוססי Claude על אותה מכונה.
Failure modes, with the strings you will see
401 on a working key. הודעת השגיאה היא:
anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}, 'request_id': 'req_011CSHoEeqs5C35K2UUqR7Fy'}אם המפתח עובד ב-shell אך השירות מחזיר 401, השירות מעולם לא קיבל אותו — זכור ש-systemd אינו קורא את .bashrc; ודא ש-EnvironmentFile= מצביע לנתיב הנכון. סיבות נוספות: גרשיים שהודבקו בקובץ ה-env (ב-ANTHROPIC_API_KEY="sk-ant-..." — systemd מסיר את הגרשיים, אך ה-. file של ה-shell wrapper שומר אותם בתוך הערך אם השתמשת בגרשיים בצורה לא תקינה), רווחים בסוף השורה (trailing whitespace), או מפתח שביטלת ב-Console בשבוע שעבר.
404 from a model typo. המקרה הנפוץ ביותר הוא הוספת סיומת תאריך ל-model ID נוכחי:
anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-opus-4-8-20260115'}, 'request_id': 'req_011CSJqymAvNw4bT3qmDdMbA'}מזהים של הדור הנוכחי הם מדויקים בדיוק כפי שנכתבו — claude-opus-4-8, claude-haiku-4-5, claude-sonnet-5. העתק אותם מתיעוד המודלים, לעולם לא מהזיכרון או ממדריך ישן.
429 rate_limit_error. מחרוזת סוג השגיאה היא rate_limit_error והתגובה כוללת header מסוג retry-after עם מספר השניות להמתנה. ה-SDK כבר ניסה שוב פעמיים עם backoff לפני שמופיעה השגיאה, לכן 429 מתמשך אומר שהקצב המצטבר שלך אכן חורג מהמכסה של המסלול שלך — בצע batch לעבודה או פזר אותה לאורך זמן, אל תצמצם את לולאת ה-retry.
It prints the object, not the text. הפלט נראה כמו [TextBlock(citations=None, text='...', type='text')]. הדפסת את response.content במקום לעבור בלולאה על ה-blocks ולקרוא את .text מהבלוקים שבהם block.type == "text". כל דוגמת SDK לעיל מבצעת זאת נכון; העתק את הלולאה.
error: externally-managed-environment. הרצת את pip install מול ה-system Python של Ubuntu 24.04. השתמש ב-venv — לעולם אל תשתמש ב---break-system-packages בשרת שחשוב לך.
Truncated answers. response.stop_reason == "max_tokens" אומר שהמודל הגיע למכסת הפלט (output cap) באמצע המחשבה. זה מתפקד כמצופה; הגדל את המכסה באופן מכוון.
ברגע שהאפליקציה הראשונה שלך עובדת, building an AI agent with Claude הופך את קריאות ה-API הללו לסוכן (agent) המשתמש בכלים (tools).
FAQ
כמה עולה לנסות את ה-Claude API?
העלות נמוכה מאוד עבור כלי כזה. נכון ליולי 2026, ה-Opus 4.8 עולה $5 למיליון input tokens ו-$25 למיליון output tokens. אבחון לוג סטנדרטי — כמה אלפי tokens בכניסה וכמה מאות ביציאה — עולה כ-2 סנט. ב-Haiku 4.5 ($1/$5) העלות היא פחות מחצי סנט. חודש של סיכומים יומיים עולה פחות מקפה. הסיכון אינו המחיר לכל קריאה; הסיכון הוא לולאות ללא הגבלה ו-max_tokens ללא הגבלה, ולכן יש להגדיר את שניהם במפורש במדריך זה.
האם קיים מסלול חינמי ל-Claude API?
נכון ליולי 2026, אין מסלול חינמי קבוע. לפי תיעוד התמחור של Anthropic, משתמשים חדשים מקבלים סכום קטן של קרדיטים חינמיים לבדיקת ה-API — ניסוי חד-פעמי, כאשר הסכום המדויק מופיע ב-Console בעת ההרשמה — ולאחר מכן יש להטעין את החשבון. אם המטרה היא עלות שולית אפסית לכל בקשה במקום איכות מתקדמת, החלופה היא self-host an open-weight model with Ollama ולשלם באמצעות RAM במקום tokens.
איך שומרים על ה-API key בטוח בשרת?
לעולם אל תכתבו אותו בקוד, לעולם לא ב-git, לעולם לא בייצוא מ-.bashrc, ולעולם לא בהקלדה ב-shell שבו ההיסטוריה תשמור אותו. שמרו אותו בקובץ בבעלות root עם הרשאות 600. טענו אותו ברמת התהליך (per-process) — באמצעות wrapper script לשימוש אינטראקטיבי, או EnvironmentFile= עבור systemd. הגדירו מפתח אחד לכל שרת או פרויקט, כך שביטול מפתח שדלף יהיה פעולה ממוקדת ולא כריתה. אם המפתח נחשף באתר paste או ב-git commit, בטלו אותו ב-Console באופן מיידי; מחיקת ה-commit אינה מבטלת את הדליפה.
באיזה מודל Claude כדאי להתחיל?
התחילו עם claude-opus-4-8 בזמן שאתם בוחנים אם הפלטים מספיק טובים לבנייה עליהם — עליכם לשפוט את הרעיון באיכות מלאה, ובנפחי שימוש של תחביב הפרש המחירים הוא סנטים בלבד. לאחר שה-prompt יתייצב, הריצו את הקלטים האמיתיים שלכם על claude-haiku-4-5; עבור סיכום, סיווג ומיון לוגים, המודל לעיתים קרובות איכותי באותה מידה בחמישית מהמחיר. עברו ל-Haiku או Sonnet על בסיס מדידה, לא כברירת מחדל.