Async Python wrapper for the Claude.ai web app
Project description
Claude-API
A reverse-engineered asynchronous Python wrapper for the Claude.ai web app.
Disclaimer: This is an unofficial library that interacts with Claude.ai's internal web API.
It is not affiliated with or endorsed by Anthropic. Use responsibly.
Features
- Multi-turn Conversations — Stateful
ChatSessionobjects keep history automatically across turns. - Streaming Mode — Yield partial outputs in real time as Claude writes them.
- File Attachments — Attach local images, PDFs, and documents to any message.
- File Downloads — Download files generated by Claude's REPL/sandbox.
- Model Selection — Switch between Claude 4, 3.7, 3.5, and any future model by name.
- System Prompts — Apply custom system prompts at the conversation or single-call level.
- Session Resumption — Serialize and reload conversation metadata to continue across Python processes.
- Auto-Close — Optional inactivity timer for always-on services.
- Async-First — Built on
aiohttpfor non-blocking I/O throughout.
Table of Contents
- claude_webapi
- Features
- Table of Contents
- Installation
- Authentication
- Usage
- Initialization
- Generate content
- Generate content with files
- Upload file as bytes
- Inline attachments
- Multi-turn conversations
- Resume a previous conversation
- Delete a conversation
- Streaming mode
- Select a language model
- Download files from Claude's sandbox
- List & manage conversations
- Check for multiple reply candidates
- Logging
- Error handling
- References
- Stargazers
Installation
Requires Python 3.10 or higher.
pip install -U claude-webapi
Authentication
- Go to claude.ai and log in with your Google / email account.
- Press F12 → Application tab → Cookies →
https://claude.ai. - Copy the value of the
sessionKeycookie. - Your
organization_idis visible in thelastActiveOrgcookie, or in the URL when you open a conversation:https://claude.ai/chat/<org_uuid>/…
Note: Keep your
sessionKeysecret — it grants full access to your Claude account.
Usage
Initialization
import asyncio
from claude_webapi import ClaudeClient
SESSION_KEY = "sk-ant-…" # your sessionKey cookie
ORGANIZATION_ID = "xxxxxxxx-…" # your org UUID
async def main():
client = ClaudeClient(SESSION_KEY, ORGANIZATION_ID)
await client.init(timeout=30, auto_close=False, close_delay=300)
# … use client …
await client.close()
asyncio.run(main())
Or use it as an async context manager (automatically calls init and close):
async def main():
async with ClaudeClient(SESSION_KEY, ORGANIZATION_ID) as client:
response = await client.generate_content("Hello!")
print(response.text)
Tip: In long-running services (bots, APIs) set
auto_close=Trueand a reasonableclose_delayso the HTTP session is cleaned up during idle periods.
Generate content
async def main():
response = await client.generate_content("Explain quantum entanglement simply.")
print(response.text)
print(response)produces the same output —ModelOutput.__str__returnstext.
Generate content with files
Pass a list of local file paths alongside your prompt:
from pathlib import Path
async def main():
response = await client.generate_content(
"Summarise this PDF and describe the chart image.",
files=["report.pdf", Path("chart.png")],
)
print(response.text)
Supported file types mirror what Claude.ai accepts: PDFs, plain text, images (PNG/JPEG/GIF/WebP), CSV, and most common document formats.
Upload file as bytes
When the file lives in memory rather than on disk, pass raw bytes directly:
async def main():
data = b"col1,col2\n1,2\n3,4"
chat = client.start_chat()
fid = await client.upload_file(
chat.cid,
data=data,
filename="data.csv",
mime_type="text/csv",
)
r = await chat.send_message("Analyse the uploaded CSV.", files=[fid])
print(r.text)
Inline attachments
Pass pre-extracted text directly as an attachment — no upload round-trip needed:
async def main():
response = await client.generate_content(
"Summarise the document below.",
attachments=[{
"extracted_content": "The quick brown fox…",
"file_name": "notes.txt",
"file_size": 1234,
"file_type": "txt",
}],
)
print(response.text)
attachments is supported on generate_content, generate_content_stream, and ChatSession.send_message / send_message_stream.
Multi-turn conversations
Use start_chat() to create a ChatSession that automatically threads context across messages:
async def main():
chat = client.start_chat()
r1 = await chat.send_message("My name is Alice.")
print(r1.text)
r2 = await chat.send_message("What is my name?")
print(r2.text) # → "Your name is Alice."
Files can be attached to any turn:
r3 = await chat.send_message(
"Analyse this spreadsheet and create a bar chart.",
files=["sales_q1.csv"],
)
Resume a previous conversation
Save chat.metadata and pass it back to start_chat to resume later — even after the Python process has exited:
import json
async def main():
chat = client.start_chat()
await chat.send_message("Remember: the secret word is BANANA.")
# Persist metadata
saved = chat.metadata
with open("session.json", "w") as f:
json.dump(saved, f)
# --- later, in a new process ---
with open("session.json") as f:
saved = json.load(f)
previous_chat = client.start_chat(metadata=saved)
r = await previous_chat.send_message("What was the secret word?")
print(r.text) # → "The secret word is BANANA."
Delete a conversation
async def main():
chat = client.start_chat()
await chat.send_message("This is temporary.")
await client.delete_conversation(chat.cid)
print(f"Deleted: {chat.cid}")
# Or equivalently:
# await chat.delete()
Streaming mode
Get incremental output using generate_content_stream or ChatSession.send_message_stream.
The text_delta attribute on each chunk holds only the new characters since the last yield.
async def main():
async for chunk in client.generate_content_stream(
"Write a 500-word short story about a time-travelling librarian."
):
print(chunk.text_delta, end="", flush=True)
print()
Inside a chat session:
async def main():
chat = client.start_chat()
async for chunk in chat.send_message_stream("Explain async/await in Python."):
print(chunk.text_delta, end="", flush=True)
print()
# Follow-up works normally — context is preserved
r = await chat.send_message("Give me a code example.")
print(r.text)
Select a language model
Pass a Model enum member or a raw model string:
from claude_webapi.constants import Model
async def main():
# Using the enum
r1 = await client.generate_content(
"What model are you?",
model=Model.OPUS,
)
print(r1.text)
# Using a raw string (useful for models not in the enum)
r2 = await client.generate_content(
"What model are you?",
model="claude-sonnet-4-6",
)
print(r2.text)
# Per-session model
chat = client.start_chat(model=Model.HAIKU)
r3 = await chat.send_message("Fast reply please.")
print(r3.text)
Available models (as of February 2026):
| Enum constant | Model string |
|---|---|
Model.SONNET |
claude-sonnet-4-6 |
Model.OPUS |
claude-opus-4-6 |
Model.HAIKU |
claude-haiku-4-5-20251001 |
Model.SONNET_3_7 |
claude-3-7-sonnet-20250219 |
Model.SONNET_3_5 |
claude-3-5-sonnet-20241022 |
Model.HAIKU_3_5 |
claude-3-5-haiku-20241022 |
Model.OPUS_3 |
claude-3-opus-20240229 |
You can always pass a custom string to access models not listed above.
Download files from Claude's sandbox
When Claude generates a file through its code interpreter (REPL), you can download it:
async def main():
chat = client.start_chat()
await chat.send_message(
"Generate a CSV with the first 20 Fibonacci numbers and save it as fib.csv"
)
local_path = await client.download_file(
chat.cid, "fib.csv", dest="./downloads"
)
print(f"Saved to: {local_path}")
List & manage conversations
async def main():
conversations = await client.list_conversations()
for conv in conversations[:5]:
print(conv["uuid"], conv.get("name", "(unnamed)"))
# Rename a conversation
await client.rename_conversation(conversations[0]["uuid"], "My renamed chat")
Check for multiple reply candidates
Claude sometimes returns multiple reply candidates. You can inspect them and select which one to continue the conversation from:
async def main():
chat = client.start_chat()
response = await chat.send_message("Recommend a sci-fi novel.")
for i, candidate in enumerate(response.candidates):
print(f"--- Candidate {i} ---")
print(candidate.text[:200])
if len(response.candidates) > 1:
chat.choose_candidate(index=1) # use the second candidate going forward
follow_up = await chat.send_message("Tell me more about that book.")
print(follow_up.text)
Logging
from claude_webapi import set_log_level
set_log_level("DEBUG") # DEBUG | INFO | WARNING | ERROR | CRITICAL
Error handling
from claude_webapi import (
ClaudeClient,
AuthenticationError,
APIError,
QuotaExceededError,
TimeoutError,
ConversationNotFoundError,
)
async def main():
try:
response = await client.generate_content("Hello!")
except AuthenticationError:
print("Invalid or expired sessionKey — please re-authenticate.")
except QuotaExceededError:
print("Daily message limit reached. Try again later.")
except TimeoutError:
print("Request timed out.")
except ConversationNotFoundError:
print("Conversation UUID not found.")
except APIError as e:
print(f"API error {e.status_code}: {e}")
References
- Anthropic — Claude.ai
- Anthropic — Official API
- gemini_webapi — Inspiration for this project's interface design
Stargazers
If this project helped you, please consider starring it ⭐
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_webapi-1.0.0.tar.gz.
File metadata
- Download URL: claude_webapi-1.0.0.tar.gz
- Upload date:
- Size: 22.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f3c5755cfb8f1770d3578c7346e25d74c7581ab0374e2cdc847c472709e984db
|
|
| MD5 |
5074bf6a0feb1424d54e41baca12bf9d
|
|
| BLAKE2b-256 |
c3ac32233e3a3022499b22dcc7df145a9275a107e98f8d7bb1ebf74463687930
|
Provenance
The following attestation bundles were made for claude_webapi-1.0.0.tar.gz:
Publisher:
python-publish.yml on cyber-wojtek/Claude-API
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_webapi-1.0.0.tar.gz -
Subject digest:
f3c5755cfb8f1770d3578c7346e25d74c7581ab0374e2cdc847c472709e984db - Sigstore transparency entry: 1540447151
- Sigstore integration time:
-
Permalink:
cyber-wojtek/Claude-API@d417afb15020c4cf638ef1934778c1165e21f7fa -
Branch / Tag:
- Owner: https://github.com/cyber-wojtek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@d417afb15020c4cf638ef1934778c1165e21f7fa -
Trigger Event:
release
-
Statement type:
File details
Details for the file claude_webapi-1.0.0-py3-none-any.whl.
File metadata
- Download URL: claude_webapi-1.0.0-py3-none-any.whl
- Upload date:
- Size: 20.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d90190e8896c57d1af9e367f5c095569b79f612cc4006382aff544fd574d62ae
|
|
| MD5 |
73efe2dd89445a9694fa2a6dabf80606
|
|
| BLAKE2b-256 |
34de39199991c208752733931bc73e8d82b1f5a3af804ae097d852c907229924
|
Provenance
The following attestation bundles were made for claude_webapi-1.0.0-py3-none-any.whl:
Publisher:
python-publish.yml on cyber-wojtek/Claude-API
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_webapi-1.0.0-py3-none-any.whl -
Subject digest:
d90190e8896c57d1af9e367f5c095569b79f612cc4006382aff544fd574d62ae - Sigstore transparency entry: 1540447260
- Sigstore integration time:
-
Permalink:
cyber-wojtek/Claude-API@d417afb15020c4cf638ef1934778c1165e21f7fa -
Branch / Tag:
- Owner: https://github.com/cyber-wojtek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@d417afb15020c4cf638ef1934778c1165e21f7fa -
Trigger Event:
release
-
Statement type: