Interview guides · OpenAI
OpenAI Software Engineer Interview Guide 2026
Prepare for OpenAI software interviews with a tested token bucket, a durable streaming-job design, failure traces and product or infrastructure prep routes.
Interviewing at OpenAI
- Practice bank: 252+ questions
- Rounds: 4
- Typical prep: 2–4 weeks
- Interview reports: 47
Interview rounds
| Round | Questions |
|---|---|
| HR Screen | 12 |
| Online Assessment | 2 |
| Technical Screen | 156 |
| Onsite | 82 |
How hard is the OpenAI Software Engineer interview?
| Difficulty | Share | Questions |
|---|---|---|
| Easy | 3% | 7 |
| Medium | 54% | 135 |
| Hard | 44% | 110 |
What OpenAI actually tests for
| Topic | Share | Questions |
|---|---|---|
| System Design | 41% | 104 |
| Coding & Algorithms | 29% | 74 |
| Software Engineering Fundamentals | 16% | 40 |
| Behavioral & Leadership | 7% | 17 |
| ML System Design | 4% | 11 |
| Machine Learning | 2% | 5 |
| Data Manipulation (SQL/Python) | 0% | 1 |
For an OpenAI software-engineering role, preparation is more useful when a coding solution and a design answer can be inspected. This guide supplies a token bucket with explicit concurrency assumptions and an evaluation-job API that can stream progress, survive a disconnected client and recover from a worker crash. Both are original practice exercises.
Use the OpenAI practice collection for additional topics, then select product/API or infrastructure preparation according to the actual posting.
Clarify the role before building the study plan
The official interview guide is the source for the company’s general process; the recruiter and invitation establish your role’s specific assessments. Its example sequence does not prove that every software candidate receives every type of interview described in an older preparation guide. Ask about languages, allowed resources, assessment format and project discussion. OpenAI interview guide.
Prepare one project at three depths: a two-minute account of the user problem and outcome, a ten-minute architecture explanation and a detailed discussion of one component or failure. Name your contribution separately from collaborators’ work. If a design involved a model, explain evaluation quality and operational behavior as well as the model choice.
For coding, state the contract before implementing. The rate limiter below has one bucket in one process and uses a monotonic clock. Those assumptions are deliberately narrow. A distributed service, per-user quotas and overload behavior require additional decisions; a lock inside one object does not enforce a global quota.
A complete token-bucket coding exercise
Use this original problem to practice translating a policy into code. One user has a bucket with capacity three tokens. Tokens refill continuously at one per second. A request consumes one token; a denied request consumes none. The bucket starts full. Time is monotonic. This is a local teaching implementation, not an OpenAI interview question or a distributed rate limiter.
import math
import threading
import time
class TokenBucket:
def __init__(self, capacity, refill_per_second, clock=time.monotonic):
for value in (capacity, refill_per_second):
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise ValueError("capacity and refill must be numbers")
if not math.isfinite(value) or value <= 0:
raise ValueError("capacity and refill must be finite and positive")
if capacity < 1:
raise ValueError("capacity must hold at least one request")
self.capacity = float(capacity)
self.rate = float(refill_per_second)
self.tokens = float(capacity)
self.clock = clock
self.last = clock()
self.lock = threading.Lock()
def allow(self):
with self.lock:
now = self.clock()
if now < self.last:
raise ValueError("clock moved backwards")
self.tokens = min(self.capacity, self.tokens + (now - self.last) * self.rate)
self.last = now
if self.tokens < 1:
return False
self.tokens -= 1
return True
now = [0.0]
bucket = TokenBucket(3, 1, clock=lambda: now[0])
assert [bucket.allow() for _ in range(4)] == [True, True, True, False]
now[0] = 0.5
assert bucket.allow() is False
now[0] = 1.0
assert bucket.allow() is True
now[0] = 100.0
assert [bucket.allow() for _ in range(4)] == [True, True, True, False]
Refill and consumption share one lock, so concurrent threads using this bucket cannot all observe the same last token and spend it. The calculation takes O(1) time per attempt and O(1) state per bucket, excluding time waiting for the lock. A dictionary of user buckets would grow with the number of retained users; expiry and a memory policy are separate requirements. Multiple processes would each have independent quotas. A shared service or an atomic operation against shared state is necessary for a global policy.
Test the race rather than merely saying “thread-safe”: create a fresh bucket with a fixed clock and capacity three, release twenty workers at a barrier, collect the results and assert exactly three accepted requests. Also reject zero capacity, negative refill and a backward-moving injected clock. The fixed clock makes the concurrency check about consumption rather than refill timing. For production, define availability, fairness, clock behavior and what happens when the quota store fails.
Design case: an evaluation API that can stream progress
Here is a complete fictional design prompt: customers submit evaluation jobs, receive progress events and retrieve a durable final result. A job can take several seconds, a client can disconnect, and a worker can crash. Start by clarifying whether progress must be exactly replayable, whether partial results are useful, and how cancellation interacts with completed work.
Use POST /evaluation-jobs with a tenant-scoped idempotency key and a payload hash. Return 202 with a job ID for a new accepted job; a retry with the same key and payload returns the same ID. Reusing that key with a different payload returns a conflict. GET /evaluation-jobs/{id} returns durable state. GET /evaluation-jobs/{id}/events?after=sequence resumes an event stream. POST /evaluation-jobs/{id}/cancel requests cancellation. Every endpoint derives authorization from the authenticated tenant.
Persist a job record before acknowledging acceptance. Write its queue request through a transactional outbox, then dispatch it. Workers acquire a lease and a generation number. Updates are conditional on that generation so an expired worker cannot overwrite the replacement worker’s result. A retry may repeat computation; the authoritative final-state transition and result reference must be idempotent. “Exactly once” is not a useful promise for arbitrary external side effects.
Store events with (job_id, sequence) and return their last sequence. If a client disconnects, processing continues and it can resume from its checkpoint. Bound event retention and document when only the final result remains available. Apply backpressure or summarize repetitive progress events rather than keeping an unbounded queue for every slow client. Cancellation is a requested state checked at safe points; define the race with completion so one terminal result wins.
For an illustrative load of 120 accepted jobs per second and a mean execution time of eight seconds, Little’s law gives about 960 jobs in execution at steady state. At 1.5 times that concurrency, plan for 1,440 execution slots before considering bursts or variable runtimes. If each job emits twenty 400-byte progress events, the raw stream payload is 960,000 bytes per second; replication, indexes and protocol overhead add more. These are assumptions for the exercise, not OpenAI’s workload or infrastructure costs.
Check duplicate submissions, lease expiry, worker crashes after result upload, cancellation during completion, a reconnect after retention expires and cross-tenant access. Explain which checks require integration tests against real storage. An in-memory mock does not prove lease correctness or transactional behavior.
Match preparation to the role, then confirm the loop
For product/API work, practice API contracts, tenant isolation, retries and user-visible failure behavior. For infrastructure work, add capacity, leases, rollout and incident recovery. For ML-facing engineering, discuss reproducibility, dataset versions, evaluation quality and cost. These are editorial routes; confirm the actual role and assessment format with your recruiter.
As checked on 8 October 2026, OpenAI’s public interview guide describes role-specific assessments and a final interview format that can vary. Its general examples are not a guaranteed schedule for every software team. OpenAI interview guide.
Run the fixed-clock concurrency check
The following check exercises the consumption race rather than relying on a claim about the lock. It uses the TokenBucket defined above. Twenty threads wait at a barrier before attempting admission with a clock fixed at zero, so refill cannot change the expected count.
from concurrent.futures import ThreadPoolExecutor
bucket = TokenBucket(3, 1, clock=lambda: 0.0)
barrier = threading.Barrier(20)
def attempt(_):
barrier.wait()
return bucket.allow()
with ThreadPoolExecutor(max_workers=20) as workers:
results = list(workers.map(attempt, range(20)))
assert sum(results) == 3
The supplied check was executed during this revision and admitted exactly three requests. That is meaningful evidence for this local fixture, not a proof of every scheduling interleaving or a distributed service. Test the policy at its intended boundary before claiming broader guarantees.
Trace the streaming design through a crash
Suppose worker generation 7 uploads a result, then loses its lease before committing completion. Generation 8 starts. The durable job record still does not say complete. The replacement can discover or recompute the result according to the storage contract, while generation 7 must fail a conditional state update because it no longer owns the job. A result object’s existence alone should not establish the job’s authoritative terminal state.
For idempotent acceptance, persist both the tenant-scoped key and payload hash. A retry with the same payload returns the existing job ID; a different payload must not silently attach to that job. Decide how long keys are retained. Deleting an idempotency record too early can allow a late retry to create duplicate work.
For an event reconnect, a client sends its last sequence. Return subsequent retained events in order. If the checkpoint is older than retention, return a defined recovery response and point to durable status or the final result. Do not claim resumability while discarding the only history needed to resume.
Name the metrics that diagnose the design
Track admission denials, queue wait, execution time, lease replacements, failed conditional updates, event-delivery lag and final-state latency. Separate a slow model call from a client that reads slowly. A low average execution time can coexist with a growing queue if arrivals exceed the service’s useful capacity.
For the rate limiter, define whether clients receive a retry estimate and how that estimate is calculated. For the job API, explain whether overload rejects new jobs or accepts them into a bounded queue. Both are user-visible contracts. More workers can increase throughput only if the constrained dependency, quota or resource also permits it.
Explain the trade-off with numbers you can defend
In the evaluation design, a shorter mean execution time reduces steady-state concurrency only if the arrival rate and workload remain comparable. Tail runtimes, queued work and bursts still matter. The 1,440-slot figure is an illustrative margin over the stated average model; it is not a measured sizing result. State what you would measure before choosing a production limit.
For product/API work, describe user-visible acceptance, progress, completion and cancellation. For infrastructure, explain resource admission, leases, worker failure and observability. A strong answer connects the storage transition to the customer promise: a returned job ID should be recoverable, and a repeated request should not create a second job silently.
A compact preparation week
Implement the bucket and boundary tests, add the fixed-clock concurrency check, trace duplicate submissions in the design, trace a worker crash, explain a real project, run a timed coding-plus-design mock and repeat the weakest part. After each session, keep an artifact: running checks, an API contract, a failure trace or a corrected project explanation. Avoid spending the entire week polishing a company-motivation speech.
Does a local lock solve distributed rate limiting?
No. It protects callers sharing this object in one process. A global policy needs shared state or a service with an atomic admission operation and a defined failure policy.
Must every software candidate know model-training internals?
Match depth to the role. Product engineering, platform infrastructure and ML-facing work can have different expectations. Use the posting and contact’s guidance to decide what to prepare.
Can I describe the streaming API as exactly once?
Be precise about which state transition is idempotent and which work may repeat. A worker may recompute after a crash; external side effects need their own guarantees. “Exactly once” without a boundary hides the important failure case.
Sources and Further Reading
Editorial revision: 8 October 2026. Current hiring details remain role-specific; unavailable source pages and unverified timings are identified in the text.