Skip to content

API Integration Engineer Interview Questions & Answers

40 questions for API Integration Engineer interviews — 2 easy, 29 medium, 9 hard.

About the API Integration Engineer role

Connects the company's platform with partners, payment gateways, logistics providers and SaaS tools. Builds webhook receivers, signature verification, retry and reconciliation jobs, and clear API documentation for external developers. Strong focus on reliability, idempotency, error handling and monitoring, since integration failures directly affect revenue and customer experience.

Webhooks OpenAPI HMAC signatures Retries and backoff Background jobs
Solved for this roleStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

Explain the difference between Promise.all, Promise.allSettled, Promise.race and Promise.any.

Save to ▾
Show answer
Promise.all resolves with all results when every promise fulfils, and rejects as soon as one rejects. Promise.allSettled waits for all and returns each status and value or reason, useful when partial failure is fine. Promise.race settles with the first promise to settle, often used for timeouts. Promise.any resolves with the first fulfilled promise and rejects only if all reject.
02

How can you cancel an in-flight HTTP request in Node.js?

Save to ▾
Show answer
Use AbortController. Create a controller, pass controller.signal to fetch or to libraries that support it such as axios, and call controller.abort() when the request is no longer needed, for example when the client disconnects. AbortSignal.timeout(ms) gives a ready-made timeout signal. Aborting rejects the promise with an AbortError, which you should handle separately from real failures.
03

A scheduled job processes orders with setInterval every minute, but sometimes runs overlap and duplicate emails are sent. How do you fix it?

Save to ▾
Show answer
setInterval fires regardless of whether the previous async run finished. Use a recursive setTimeout that schedules the next run only after the current one completes, or keep an isRunning flag. If several instances run, use a distributed lock in Redis or a job queue like BullMQ with unique job IDs. Also make email sending idempotent by recording sent message IDs.
04

What is the OWASP Top 10 and which items matter most for Node.js APIs?

Save to ▾
Show answer
The OWASP Top 10 lists the most critical web application security risks. For Node APIs the most relevant are broken access control, injection, cryptographic failures, security misconfiguration, vulnerable and outdated components, authentication failures and server-side request forgery. OWASP also publishes an API Security Top 10, which highlights broken object level authorisation, where users access others' data by changing an ID.
05

How does HTTP caching with ETag and Cache-Control work for APIs?

Save to ▾
Show answer
Cache-Control tells clients and CDNs how long a response is fresh, for example public, max-age=300, or private, no-store for user data. An ETag is a hash of the response; the client sends it back in If-None-Match, and if nothing changed the server returns 304 Not Modified with no body. Express generates weak ETags automatically for res.send responses.
06

Implement cursor-based pagination for a products endpoint.

Save to ▾
Show answer
router.get('/products', async (req, res) => { const limit = Math.min(Number(req.query.limit) || 20, 100); const cursor = req.query.cursor; const filter = cursor ? { _id: { $lt: cursor } } : {}; const items = await Product.find(filter) .sort({ _id: -1 }) .limit(limit + 1); const hasMore = items.length > limit; if (hasMore) items.pop(); res.json({ data: items, nextCursor: hasMore ? items[items.length - 1]._id : null }); });
07

How do you implement role-based access control (RBAC) in an API?

Save to ▾
Show answer
Define roles such as admin, manager and customer, and map them to permissions like order:read or order:refund. Put the role or permissions in the token or load them per request, and check them in middleware before handlers. For ownership rules, also check resource data, for example a customer can view only orders where order.userId equals req.user.id. Test both allowed and denied paths.
08

How do you handle a breaking API change request from a product manager close to a release?

Save to ▾
Show answer
I would first understand why it is needed and who consumes the API. Then I explain the impact on existing clients, propose a non-breaking path such as adding a new field or a v2 endpoint, and estimate effort and risk. Result in a past case: we shipped the additive change on time and scheduled removal of the old field for the next quarter.
09

A test passes locally but fails randomly in CI. How do you approach this flaky test?

Save to ▾
Show answer
Run it repeatedly to reproduce, then look for shared state between tests, missing awaits, reliance on test order, real timers, current date or time zones, random data and network calls. Fix by isolating data per test, using fake timers, freezing dates, mocking external calls and awaiting all promises. Quarantine the test only temporarily with a ticket, never delete it silently.
10

A security audit finds that users can see other users' orders by changing the order ID in the URL. How do you fix it?

Save to ▾
Show answer
This is broken object level authorisation. Every query must be scoped to the authenticated user: Order.findOne({ _id: id, userId: req.user.id }), returning 404 if not found. Put this check in a shared service or middleware so no route can skip it, add tests that try other users' IDs, and consider non-guessable IDs like UUIDs as defence in depth, not as the fix.
11

What is NoSQL injection in MongoDB and how do you prevent it?

Save to ▾
Show answer
If you pass req.body directly into a query, an attacker can send { "password": { "$ne": null } } and bypass checks. Prevent it by validating and casting input types with zod or Joi, rejecting objects where strings are expected, enabling Mongoose strictQuery and sanitizeFilter, and never building queries from raw user objects. Avoid $where and server-side JavaScript evaluation.
12

Write a retry function with exponential backoff for a flaky API call.

Save to ▾
Show answer
async function retry(fn, retries = 3, baseMs = 200) { for (let attempt = 0; ; attempt++) { try { return await fn(); } catch (err) { if (attempt >= retries) throw err; const delay = baseMs * 2 ** attempt + Math.random() * 100; await new Promise((r) => setTimeout(r, delay)); } } } await retry(() => paymentClient.getStatus(id));
13

A dashboard endpoint makes five independent database queries one after another and takes 1.5 seconds. How would you speed it up?

Save to ▾
Show answer
Because the queries are independent, run them concurrently with Promise.all so total time is close to the slowest query instead of the sum. Check that the database connection pool is large enough to serve them in parallel. Then add indexes for slow queries, cache rarely changing parts in Redis, and use Promise.allSettled if the page can render with some widgets missing.
14

How do you implement filtering and sorting safely from query parameters?

Save to ▾
Show answer
const ALLOWED_SORT = ['price', 'createdAt', 'name']; router.get('/products', async (req, res) => { const filter = {}; if (req.query.category) filter.category = String(req.query.category); if (req.query.maxPrice) filter.price = { $lte: Number(req.query.maxPrice) }; const field = ALLOWED_SORT.includes(req.query.sort) ? req.query.sort : 'createdAt'; const dir = req.query.order === 'asc' ? 1 : -1; res.json(await Product.find(filter).sort({ [field]: dir }).limit(50)); }); // Whitelist fields and cast types to block injection.
15

How do you implement rate limiting on a login endpoint?

Save to ▾
Show answer
const rateLimit = require('express-rate-limit'); const { RedisStore } = require('rate-limit-redis'); const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, limit: 5, standardHeaders: 'draft-7', legacyHeaders: false, keyGenerator: (req) => `${req.ip}:${req.body.email}`, store: new RedisStore({ sendCommand: (...a) => redis.sendCommand(a) }) }); app.post('/auth/login', loginLimiter, login); // Add account lockout or CAPTCHA after repeated failures.
16

What is the difference between throwing an error and rejecting a promise inside an async function?

Save to ▾
Show answer
Inside an async function they behave the same: throw new Error() causes the returned promise to reject with that error. Outside async functions, a throw inside a plain callback such as setTimeout is not caught by promise handlers and becomes an uncaught exception. Always reject with Error objects, not strings, so you keep a stack trace.
17

How do you prevent server-side request forgery when your API fetches a user-supplied URL?

Save to ▾
Show answer
An attacker could make your server call internal addresses such as 169.254.169.254 for cloud metadata or localhost admin ports. Allow only http and https, resolve the hostname and block private, loopback and link-local IP ranges, use an allowlist of domains where possible, disable redirects or revalidate them, set short timeouts and size limits, and run fetchers in an isolated network.
18

Explain OAuth 2.0 and the Authorization Code flow with PKCE.

Save to ▾
Show answer
OAuth 2.0 lets an app access resources on a user's behalf without seeing their password, for example social login with an existing account. In Authorization Code with PKCE, the app creates a code verifier and sends its hash to the authorisation server. The user logs in and consents, the app receives a code and exchanges it plus the verifier for tokens. PKCE stops intercepted codes being reused.
19

How do you design a consistent error response format for an API?

Save to ▾
Show answer
Use one structure everywhere, for example { "error": { "code": "VALIDATION_FAILED", "message": "Email is invalid", "details": [{ "field": "email" }], "requestId": "abc123" } }. Many teams follow RFC 9457 Problem Details with type, title, status and detail. Use machine-readable codes for clients, human messages for display, and never leak stack traces or SQL errors.
20

How does async/await work under the hood?

Save to ▾
Show answer
An async function always returns a promise. await pauses that function until the awaited promise settles, then resumes it with the value or throws the rejection, while the event loop keeps running other work. Internally it is built on promises and generator-like suspension. The code reads like synchronous code but is still non-blocking, and errors are handled with ordinary try/catch.
21

What is the difference between REST and GraphQL?

Save to ▾
Show answer
REST exposes multiple endpoints with fixed response shapes, uses HTTP caching easily and is simple to reason about. GraphQL exposes one endpoint where clients query exactly the fields they need, reducing over-fetching and multiple round trips, which helps mobile apps. GraphQL adds complexity: N+1 query risks needing DataLoader, harder caching, query cost limits and schema governance.
22

What is HATEOAS and is it used in real projects?

Save to ▾
Show answer
HATEOAS, Hypermedia as the Engine of Application State, means responses include links telling the client what it can do next, for example an order response with links to cancel or pay. It makes APIs more discoverable and decoupled. In practice most JSON APIs only partly use it, such as pagination links, because clients are usually built against documented endpoints.
23

What are XSS and CSRF and how do you protect a Node.js app from them?

Save to ▾
Show answer
XSS injects malicious script into pages other users view; prevent it by escaping output, using templating or React auto-escaping, sanitising rich HTML with DOMPurify and setting a strong Content-Security-Policy. CSRF tricks a logged-in browser into sending unwanted requests; prevent it with SameSite cookies, CSRF tokens for cookie-based auth and checking Origin headers on state-changing requests.
24

Explain debounce and throttle and where they matter on the backend.

Save to ▾
Show answer
Debounce runs a function only after calls stop for a set time, merging bursts into one call. Throttle runs it at most once per interval. On the backend they help when batching cache invalidation, limiting webhook processing, writing metrics, or reacting to file watcher events. For API traffic you normally use a rate limiter instead, which is a server-side form of throttling.
25

Your JWT secret has leaked. What steps do you take?

Save to ▾
Show answer
Treat it as an incident. Generate a new secret and deploy it immediately, which invalidates all existing tokens and forces re-login. Revoke refresh tokens in the database, check logs for suspicious tokens or admin actions, and find how it leaked, for example a committed .env or logs. Move to a secrets manager, consider asymmetric RS256 keys with key IDs to rotate safely, and document the incident.
26

Tell me about a time you had to debug a tricky asynchronous bug.

Save to ▾
Show answer
Situation: customers occasionally received two invoices. Task: I had to find why it happened only in production. Action: I added request IDs to logs and found a forEach with async callbacks plus a retry triggering a second save before the first finished. I replaced it with for...of, added a unique index on invoice number and made the job idempotent. Result: duplicates stopped completely.
27

Tell me about a time a database change caused an issue and what you learned.

Save to ▾
Show answer
Situation: I added a NOT NULL column with a default to a large MySQL table during business hours. Task: the migration locked the table and checkout slowed down. Action: I rolled back, rewrote it as add nullable column, backfill in batches, then add the constraint, and ran it at low traffic after testing on a production-size copy. Result: no downtime, and we added a migration review checklist.
28

Compare session-based authentication with JWT-based authentication.

Save to ▾
Show answer
Sessions store state on the server, often in Redis, and the client holds only a session ID cookie; logout and revocation are easy. JWTs are stateless, so any service can verify them without a lookup, which suits microservices and mobile apps, but revoking a token before expiry needs a denylist or short lifetimes. Many systems combine short-lived JWT access tokens with server-stored refresh tokens.
29

Write code to verify an HMAC signature on an incoming webhook in Express.

Save to ▾
Show answer
const crypto = require('crypto'); app.post('/webhooks/payment', express.raw({ type: 'application/json' }), (req, res) => { const sig = req.get('X-Signature') || ''; const expected = crypto .createHmac('sha256', process.env.WEBHOOK_SECRET) .update(req.body) .digest('hex'); const ok = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); if (!ok) return res.status(401).end(); handleEvent(JSON.parse(req.body)); res.status(200).end(); });
30

Your API's p99 latency is high during peak hours even though CPU is only 40 percent. What could be wrong?

Save to ▾
Show answer
Low CPU with high latency suggests waiting, not computing. Check database query times and connection pool wait, slow downstream APIs, Redis latency, DNS lookups without keep-alive agents, garbage collection pauses and event loop lag from occasional heavy requests. Use distributed tracing with OpenTelemetry to see where time goes, then add indexes, caching, timeouts or more pool connections.
31

Why is using async callbacks inside forEach a common bug?

Save to ▾
Show answer
Array.forEach ignores the promise returned by an async callback, so it does not wait for any iteration and errors are not caught by the outer function. Code after the loop runs before the work is done. Use for...of with await for sequential processing, or map the items to promises and await Promise.all for parallel processing.
32

Tell me about an API you designed that you are proud of.

Save to ▾
Show answer
Situation: our support team used spreadsheets to track refunds. Task: I designed a refund API used by the admin panel and payment service. Action: I wrote the OpenAPI spec first, reviewed it with frontend and finance, added idempotency keys, clear error codes and pagination, and wrote contract tests. Result: integration took two days instead of a week, and refund errors dropped sharply.
33

What are the common strategies for API versioning?

Save to ▾
Show answer
URL versioning like /api/v1/orders is the most common and easy to route and cache. Header versioning uses a custom header or Accept media type, keeping URLs clean but less visible. Query versioning like ?version=2 is simple but messy. Whatever you choose, version only for breaking changes, support old versions for a deprecation period and announce sunset dates.
34

What are async iterators and when would you use for await...of?

Save to ▾
Show answer
An async iterator returns promises from its next() method, and for await...of consumes them one by one. In Node, readable streams, readline interfaces and many database cursors are async iterable. It is ideal for processing paginated API results or large query cursors item by item with natural backpressure, since the next chunk is not pulled until the current one is handled.
35

What happens if you forget to await a promise inside a try/catch?

Save to ▾
Show answer
If you call an async function without await, the try/catch only covers the synchronous part, so a later rejection escapes it and becomes an unhandled rejection that can crash the process. The function also continues before the work finishes, causing race conditions like responding before data is saved. ESLint rules such as no-floating-promises in TypeScript help catch this.
36

Write signup and login handlers using bcrypt.

Save to ▾
Show answer
const bcrypt = require('bcrypt'); async function signup(req, res) { const hash = await bcrypt.hash(req.body.password, 12); const user = await User.create({ email: req.body.email, password: hash }); res.status(201).json({ id: user.id }); } async function login(req, res) { const user = await User.findOne({ email: req.body.email }).select('+password'); const ok = user && await bcrypt.compare(req.body.password, user.password); if (!ok) return res.status(401).json({ error: 'Invalid credentials' }); res.json({ token: signAccessToken(user) }); }
37

How would you add OTP-based login for an Indian mobile app?

Save to ▾
Show answer
Generate a random 6-digit OTP with crypto.randomInt, store only its hash in Redis with a 5-minute TTL and an attempt counter, and send it through an SMS provider on a DLT-registered template. Verify with a timing-safe compare, limit to about 3 to 5 attempts and rate limit sends per number and IP. On success, issue access and refresh tokens and delete the OTP.
38

A partner integration retries POST requests on timeouts and creates duplicate orders in your API. How would you redesign the endpoint?

Save to ▾
Show answer
Require an Idempotency-Key header on order creation and store it with a unique index alongside the created order ID and response. On a repeated key with the same payload, return the original response; with a different payload, return 409 or 422. Also add a unique business constraint such as partner order reference, respond quickly by queueing slow work, and document retry behaviour for partners.
39

How do you limit concurrency when calling an external API for 10,000 records?

Save to ▾
Show answer
async function runLimited(items, limit, worker) { const results = []; let i = 0; async function next() { while (i < items.length) { const idx = i++; results[idx] = await worker(items[idx]); } } await Promise.all(Array.from({ length: limit }, next)); return results; } await runLimited(records, 10, callPartnerApi); // Or use the p-limit package.
40

Why is structured logging important and which libraries are used?

Save to ▾
Show answer
Structured logs are JSON lines with fields like level, timestamp, requestId, userId and message, so tools like ELK, Loki, Datadog or CloudWatch can search and aggregate them. pino is fast and popular, winston is flexible with many transports. Log at correct levels, include a correlation ID per request, and never log passwords, tokens, full card numbers or Aadhaar numbers.

Frequently asked questions

What does a API Integration Engineer do?

Connects the company's platform with partners, payment gateways, logistics providers and SaaS tools. Builds webhook receivers, signature verification, retry and reconciliation jobs, and clear API documentation for external developers. Strong focus on reliability, idempotency, error handling and monitoring, since integration failures directly affect revenue and customer experience.

Which skills are tested in a API Integration Engineer interview?

Expect questions on Webhooks, OpenAPI, HMAC signatures, Retries and backoff, Background jobs. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a API Integration Engineer interview?

Revise REST API Design, Authentication and Security, Asynchronous JavaScript first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.