December 2025 Update: Production patterns for processing thousands of LLM requests efficiently while respecting rate limits and managing costs.
Why Batch Processing?
Every LLM application eventually hits the moment where you need to process not 10 requests, but 10,000. Maybe you’re embedding an entire document corpus, classifying a backlog of support tickets, or generating product descriptions for a catalog. At this scale, sequential processing is not slow — it’s comically impractical. The math makes this concrete: if each LLM call takes 3 seconds and you have 10,000 prompts, sequential processing takes 8.3 hours. With batched concurrent processing (20 workers), that drops to about 25 minutes. With the OpenAI Batch API, you submit the whole thing and come back in a few hours — at 50% the cost.OpenAI Batch API
OpenAI’s Batch API is a fundamentally different approach: instead of making real-time API calls, you submit a file of requests and OpenAI processes them asynchronously within a 24-hour window. The tradeoff is latency for cost — you get 50% off both input and output tokens. When to use it: anytime you don’t need results immediately. Data pipelines, nightly report generation, bulk content creation, embedding large corpora, evaluation runs — all perfect candidates. When NOT to use it: anything user-facing or time-sensitive. Think of it like the difference between express and standard shipping: same package, same destination, but standard is half the price because the carrier can optimize their route.Concurrent Processing with Rate Limiting
When you can’t wait for the Batch API (need results in minutes, not hours), the real-time concurrent pattern is your tool. This fires multiple requests simultaneously while respecting rate limits — think of it as a controlled firehose. The three knobs you’re tuning: requests per minute (RPM), tokens per minute (TPM), and max concurrent connections. Start conservative and increase. It’s far better to process a batch in 10 minutes at 80% rate utilization than to get rate-limited into a retry spiral that takes 30 minutes.Queue-Based Processing
For very large volumes (100K+ items),asyncio.gather won’t cut it — you can’t create 100K coroutines and hope for the best. The queue-based pattern decouples “submitting work” from “processing work” using a producer-consumer model with a fixed pool of workers.
This is the same pattern that powers every serious data pipeline: RabbitMQ consumers, Celery workers, SQS processors. The async queue version is lighter-weight (no external broker needed) but follows the same principles.
Chunked Processing for Large Datasets
When datasets get really large (10K+ items), processing everything as one giant batch creates problems: no progress visibility, no recovery from mid-batch failures, and potential memory issues from holding all results in memory. Chunking solves this by breaking the dataset into manageable pieces and processing each chunk independently. The inter-chunk delay is a deliberate breather. It gives the API provider time to recover between bursts and prevents your rate limiter from drifting (accumulated rounding errors can cause brief rate limit violations at chunk boundaries).Progress Tracking and Checkpointing
Checkpointing is the difference between “my script crashed at item 7,500 and I have to start over” and “my script crashed at item 7,500 and resumed from exactly where it left off.” For any batch job that takes more than 10 minutes, checkpointing is not optional — it’s a basic production requirement. The pattern is simple: periodically serialize your progress to disk. On restart, check if a checkpoint exists and resume from the last saved position. The tricky part is making this atomic — a crash during checkpoint writing shouldn’t corrupt your state.Cost Estimation
Never run a large batch without estimating cost first. This sounds obvious, but the number of teams that have accidentally burned 50 is staggering. The most common mistake: forgetting that output tokens cost 2-5x more than input tokens, and underestimating average output length. Always run a small sample (50-100 items) first to measure actual token usage, then extrapolate. The estimate below uses character-count heuristics, which are accurate to within ~20% for English text.Key Takeaways
Use Batch API
OpenAI Batch API saves 50% on costs for async workloads
Rate Limit Properly
Token bucket algorithms prevent hitting API limits
Checkpoint Progress
Save progress for recovery from failures
Estimate Costs
Always estimate before running large batches
Interview Deep-Dive
Your batch processing job of 100K LLM requests fails at item 67,000 due to a provider outage. How do you design for this?
Your batch processing job of 100K LLM requests fails at item 67,000 due to a provider outage. How do you design for this?
What interviewers are testing: Resilience engineering and operational maturity for long-running data pipelines.Strong answer: This is a checkpointing and idempotency problem. The system needs three things: progress persistence, safe resumption, and deduplication.First, checkpoint every N items (100-500 is a good range). Each checkpoint writes the last successfully processed index and all results so far to a durable store (local file, S3, or database). The checkpoint write itself should be atomic — write to a temp file, then rename — so a crash during checkpointing doesn’t corrupt state.Second, on restart, load the checkpoint and resume from
last_processed_index + 1. The items before that index are already done, so skip them. The items after that index need processing. Items in the “gap” (between last checkpoint and the crash point) might or might not have been processed — so you need idempotency.Third, for idempotency: use deterministic IDs for each request (e.g., hash of the prompt + index). Before processing an item, check if a result already exists for that ID. This makes retries safe — processing the same item twice produces the same result entry, not a duplicate.The provider outage itself should trigger a circuit breaker: after 5-10 consecutive failures, pause processing and wait with exponential backoff before probing again. Don’t burn through your retry budget on 33,000 remaining items when the provider is down.Cost protection: track cumulative spend during the batch. If it exceeds a configurable threshold (say, 150% of estimated cost), halt and alert. Provider outages can sometimes cause partial responses that still incur charges.Compare the OpenAI Batch API vs. self-managed concurrent processing. When would you choose each?
Compare the OpenAI Batch API vs. self-managed concurrent processing. When would you choose each?
What interviewers are testing: Understanding of cost-latency tradeoffs and operational complexity in real production systems.Strong answer: The decision comes down to four factors: latency requirements, cost sensitivity, operational complexity tolerance, and control needs.OpenAI Batch API: 50% cost savings, up to 24-hour completion window, zero infrastructure to manage, no rate limit concerns (OpenAI manages scheduling internally). Choose this for: nightly data pipelines, evaluation runs, content generation backlogs, embedding large corpora — anything where “done by tomorrow morning” is fast enough.Self-managed concurrent processing: Results in minutes instead of hours, full control over retry logic and error handling, ability to use multiple providers with fallback, real-time progress visibility. Choose this for: time-sensitive batch jobs (process uploaded files within 30 minutes), workflows that need partial results quickly, multi-provider strategies, or when you need custom logic like filtering mid-batch based on intermediate results.The hybrid approach often wins: use self-managed concurrency for the urgent 20% of items (new uploads, high-priority customers) and route the remaining 80% through the Batch API for cost savings. This requires a routing layer that classifies items by urgency and dispatches them to the appropriate processing path.One often-overlooked factor: the Batch API doesn’t support streaming, function calling, or vision inputs in all configurations. Verify that your specific use case is supported before committing to it for a production pipeline.
You need to process 1 million documents through an LLM for classification. The total estimated cost is $15,000. How do you approach this?
You need to process 1 million documents through an LLM for classification. The total estimated cost is $15,000. How do you approach this?
What interviewers are testing: End-to-end thinking about large-scale ML data pipelines, cost optimization, and risk management.Strong answer: At this scale, you’re operating more like a data engineering project than a simple script. The approach has four phases.Phase 1 — Validate and sample: Process a random sample of 500 documents first. Measure actual token usage (not estimates), quality of results, failure rate, and per-item cost. Extrapolate to confirm the 15K on bad results.Phase 2 — Optimize cost: Can you use a cheaper model? If gpt-4o-mini gives 95% of gpt-4o’s classification accuracy, you’ve just cut costs by 80%. Can you shorten prompts? Every token in the system message gets multiplied by 1M. Can you use the Batch API? That’s 15,000. Can you pre-filter obviously classifiable documents with a rules-based system and only send ambiguous ones to the LLM? That might eliminate 40% of calls.Phase 3 — Execute with guardrails: Split into daily batches of ~100K (spreading cost over 10 days reduces blast radius if something goes wrong). Set daily cost limits. Checkpoint every 1,000 items. Run quality spot-checks on each day’s output before proceeding to the next batch. Use the Batch API for the bulk and concurrent processing only for rush items.Phase 4 — Long-term: If this is recurring, train a fine-tuned model or a traditional ML classifier on the LLM-labeled data. A fine-tuned gpt-4o-mini is much cheaper per call, and a distilled BERT classifier running on your own hardware costs essentially nothing at inference time. The $15K LLM run becomes your labeling step, not your production system.The meta-point: at $15K, this needs a project plan, not just a script. Stakeholder buy-in on cost, rollback plans, quality gates, and a path to cost reduction over time.
How would you implement a cost-aware batch processor that automatically switches models or pauses when budget is exceeded?
How would you implement a cost-aware batch processor that automatically switches models or pauses when budget is exceeded?
What interviewers are testing: Practical cost governance for AI systems, which is an increasingly critical production concern.Strong answer: Build a cost tracker as a first-class component, not an afterthought.The CostTracker maintains a running total of actual spend (from API response usage fields, not estimates) and compares against configurable thresholds. It exposes three states: green (under 80% of budget), yellow (80-100%, switch to cheaper model), and red (over budget, pause processing).Implementation: After each API call, extract the actual token counts from the response and calculate cost using the model’s pricing. Update the running total atomically (this tracker is shared across concurrent workers). At each threshold crossing, execute the configured action.Actions at yellow threshold: Automatically downgrade remaining items from gpt-4o to gpt-4o-mini. Log the switchover for quality tracking. Alert the team via Slack/PagerDuty so humans can decide whether to increase the budget or accept the quality tradeoff.Actions at red threshold: Stop submitting new items. Let in-flight requests complete (don’t waste already-spent money). Save checkpoint. Send an alert with a summary: items completed, items remaining, actual cost vs. budget, and estimated cost to complete.The nuance most people miss: token estimation before the call is unreliable for output tokens (you don’t know how much the model will generate). So budget tracking must be based on actual usage, not pre-estimates. This means you might slightly overshoot the budget by the cost of in-flight requests when the red threshold triggers. Account for this buffer in your budget planning.
What’s Next
LLM Fallbacks
Build resilient systems with multi-provider fallback chains