Try async code interactively — including parallel Claude API calls.
Day 62 of 80
Read Anthropic SDK documentation and look for the async client section. It shows how AsyncAnthropic works and when to use it.
Jupyter supports await directly in cells — you don't need asyncio.run() in notebooks. In .py files you'll always use asyncio.run().
import asyncio
async def make_coffee(name):
print(f" Starting {name}...")
# asyncio.sleep() pauses THIS coroutine without blocking others.
# Regular time.sleep() would freeze everything.
await asyncio.sleep(2)
print(f" {name} ready!")
return f"{name} coffee"
# In Jupyter you can await directly. Each runs in sequence — 6 seconds total.
result1 = await make_coffee("Espresso")
result2 = await make_coffee("Latte")
result3 = await make_coffee("Cappuccino")
print(f"Done: {[result1, result2, result3]}")
Running them in sequence like this — await one, await two, await three — still takes 6 seconds. You're not getting any parallelism yet. The next cell fixes that.
import time
async def make_coffee(name, seconds):
print(f" Starting {name}...")
await asyncio.sleep(seconds)
print(f" {name} ready!")
return f"{name} coffee"
start = time.time()
# asyncio.gather() runs all three AT THE SAME TIME.
# Total time = time of the SLOWEST task, not sum of all tasks.
results = await asyncio.gather(
make_coffee("Espresso", 2),
make_coffee("Latte", 3),
make_coffee("Cappuccino", 1),
)
elapsed = time.time() - start
print(f"\nAll done in {elapsed:.1f}s: {results}")
# Should print: All done in ~3.0s (the slowest task)
# NOT 6.0s (the sum of all tasks)
The key insight: All three started simultaneously. The total time is determined by the slowest task (3 seconds), not the sum of all tasks (6 seconds). That's the performance gain.
import anthropic
from dotenv import load_dotenv
import time
load_dotenv()
# AsyncAnthropic is the async version of the client.
# Same API, but methods return coroutines you need to await.
client = anthropic.AsyncAnthropic()
async def generate_for_platform(shot, platform):
"""Generate a prompt for one platform — async version."""
message = await client.messages.create(
model="claude-sonnet-4-6",
max_tokens=150,
messages=[{
"role": "user",
"content": f"Write a {platform} AI video prompt for: {shot}. Under 50 words. Only the prompt."
}]
)
return {"platform": platform, "prompt": message.content[0].text}
shot = "Slow motion golf ball flight through morning mist"
start = time.time()
# Generate for all 3 platforms at once!
results = await asyncio.gather(
generate_for_platform(shot, "Kling"),
generate_for_platform(shot, "Runway"),
generate_for_platform(shot, "Veo"),
)
elapsed = time.time() - start
print(f"Generated in {elapsed:.1f}s\n")
for r in results:
print(f"[{r['platform']}]")
print(f" {r['prompt']}\n")
Run this and note the elapsed time. Compare it to running the same generation three times sequentially. The parallel version should take roughly the same time as one individual call.
# When you have many tasks, build the list first, then unpack with *
# The * before the list "unpacks" it into positional arguments.
platforms = ["Kling", "Runway", "Veo"]
shots = [
"Wide aerial over foggy mountains",
"Close-up of hands gripping steering wheel",
]
# Every shot × every platform = 6 tasks, all running in parallel
tasks = [
generate_for_platform(shot, platform)
for shot in shots
for platform in platforms
]
print(f"Running {len(tasks)} tasks in parallel...")
start = time.time()
results = await asyncio.gather(*tasks)
elapsed = time.time() - start
print(f"All {len(results)} prompts in {elapsed:.1f}s")
*tasks unpacks the list. asyncio.gather(*tasks) is equivalent to asyncio.gather(tasks[0], tasks[1], tasks[2], ...). You'll use this pattern constantly when the number of tasks isn't known at write time.
await gather() is faster than sequential awaitAsyncAnthropic and saw parallel API calls work*tasks unpack syntaxDays 63–64 build the batch_generator.py — a real tool that takes a list of shots and generates prompts for all platforms in one parallel burst.