Back to Articles

Reverse Engineering APIs with Browser DevTools: How OpenAPI DevTools Intercepts and Documents Network Traffic

[ View on GitHub ]

Reverse Engineering APIs with Browser DevTools: How OpenAPI DevTools Intercepts and Documents Network Traffic

Hook

What if you could document an entire API without reading a single line of backend code or configuration? Just browse the application normally, and watch as a complete OpenAPI specification builds itself in real-time.

Context

Every developer has encountered the nightmare of undocumented APIs. You're integrating with a third-party service, reverse engineering a legacy system, or trying to understand what your own frontend is actually calling. The traditional approach involves opening the Network tab, manually recording endpoints, guessing at request schemas, and piecing together documentation like a detective. Some teams resort to proxy tools like mitmproxy or Charles, which require network configuration and SSL certificate installation. Others dive into browser DevTools and manually copy-paste cURL commands into Postman.

OpenAPI DevTools emerged as a fundamentally different approach: what if the browser itself could be the documentation engine? By operating as a DevTools panel, it sits at the perfect intersection of visibility and convenience. It sees everything your application sends over the wire, requires zero configuration, and runs entirely in the browser. The tool watches as you interact with an application, inferring schema types from JSON payloads and automatically building an OpenAPI 3.1 specification. It's documentation through observation, turning your exploratory testing into reusable API contracts.

Technical Insight

The architecture of OpenAPI DevTools revolves around three core mechanisms: traffic interception, schema inference, and incremental specification merging. Understanding how these work together reveals both the power and constraints of browser-based API discovery.

Traffic interception happens through Chrome's webRequest API, which allows extensions to observe HTTP traffic before it reaches the page. The extension filters for JSON request and response bodies, focusing on REST API patterns while ignoring static assets. Here's a simplified view of how the interception layer might process a captured request:

chrome.webRequest.onCompleted.addListener(
  (details) => {
    if (!isJsonRequest(details)) return;
    
    const requestBody = getRequestBody(details.requestId);
    const responseBody = getResponseBody(details.requestId);
    
    const endpoint = {
      method: details.method,
      url: details.url,
      requestSchema: inferSchema(requestBody),
      responseSchema: inferSchema(responseBody),
      statusCode: details.statusCode
    };
    
    mergeIntoSpec(endpoint);
  },
  { urls: ["<all_urls>"] },
  ["requestBody", "responseHeaders"]
);

The schema inference engine is where things get interesting. When you capture a JSON payload, the extension must convert concrete values into type definitions. A response like {"userId": 42, "name": "Alice", "email": null} becomes an OpenAPI schema with integer, string, and nullable string types. The challenge intensifies with polymorphic responses—when the same field appears with different types across multiple requests.

OpenAPI DevTools handles this through schema merging with union types. If one request shows "status": "active" and another shows "status": null, the merged schema marks status as type: ["string", "null"]. This is critical for real-world APIs where nullable fields, optional properties, and varied response shapes are common. The extension maintains a running schema for each endpoint, continuously refining it as new examples arrive.

Path parameterization presents a particularly thorny problem. When you see /api/users/123 and /api/users/456, a human immediately recognizes the pattern /api/users/{id}. But automated detection is surprisingly difficult—how do you distinguish a parameter from a legitimate path segment like /api/users/search? OpenAPI DevTools takes a pragmatic approach: manual parameterization.

Users click on path segments in the UI to mark them as parameters. The extension then performs retroactive merging:

function parameterizePath(path: string, segmentIndex: number, paramName: string) {
  const segments = path.split('/');
  segments[segmentIndex] = `{${paramName}}`;
  const parameterizedPath = segments.join('/');
  
  // Find all historical requests matching this pattern
  const relatedRequests = findRequestsMatchingPattern(parameterizedPath);
  
  // Merge their schemas into a single endpoint definition
  const mergedSchema = relatedRequests.reduce((acc, req) => {
    return deepMergeSchemas(acc, req.schema);
  }, {});
  
  // Future requests to /api/users/789 now contribute to this parameterized endpoint
  registerPathPattern(parameterizedPath, mergedSchema);
}

This approach means that clicking /api/users/123 and marking 123 as {userId} will immediately consolidate all past /api/users/* requests into a single endpoint definition, and all future requests will merge into that same definition. It's a clever compromise between automation and accuracy.

The specification visualization layer uses Redoc, an open-source OpenAPI renderer, embedded directly in the DevTools panel. As you browse the application, the specification updates in real-time. You can export the generated spec as a JSON or YAML file, enabling workflows like importing into Postman, generating client SDKs with OpenAPI Generator, or seeding a documentation site.

State management happens entirely within the extension context—there's no backend, no cloud sync, no external dependencies. This is both a strength (privacy, simplicity) and a limitation (no cross-device persistence beyond manual export/import). The extension stores captured data using the chrome.storage API, with careful attention to size limits since complex applications can generate substantial specifications.

Gotcha

The project has been deprecated. This is the first and most important limitation to understand. The author has moved on to a complete rewrite called 'demystify' that addresses fundamental architectural limitations. OpenAPI DevTools will not receive bug fixes, security updates, or new features. If you're starting a new project, you should evaluate demystify instead.

Beyond deprecation, the browser-only scope creates inherent blind spots. You can only capture traffic that flows through the browser's network stack. Mobile apps, desktop applications, server-to-server communication, and WebSocket connections beyond the initial HTTP upgrade are invisible. If you're trying to document a GraphQL API, the extension will see the requests but schema inference becomes less useful since GraphQL responses are highly dynamic and query-dependent. The manual path parameterization requirement means you need to actively explore and identify patterns—it won't automatically recognize that /api/users/123, /api/posts/456, and /api/comments/789 all follow a /{resourceType}/{id} pattern. You're still doing detective work, just with better tools. And because the extension operates in the browser security context, it cannot intercept requests from other browser extensions or access request bodies for certain types of POST requests due to Chrome's security restrictions.

Verdict

Use if: You need to quickly reverse engineer a web application's API, you're documenting a third-party service that lacks OpenAPI specs, or you're exploring an internal API during development and want to bootstrap documentation. It's perfect for discovery sessions where you explore an application manually and want to capture what you find without configuration overhead. The real-time feedback and zero-setup nature make it ideal for time-boxed investigation work. Skip if: You're starting a new project (use demystify instead, which offers automatic path parameter detection and broader capture mechanisms), you need to document non-browser traffic like mobile apps or microservices, you require ongoing maintenance and support, or you're working with highly dynamic APIs like GraphQL where schema inference provides limited value. Also skip if your target application makes heavy use of WebSockets or binary protocols, which fall outside the extension's interception capabilities.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/automation/andrewwalsh-openapi-devtools.svg)](https://starlog.is/api/badge-click/automation/andrewwalsh-openapi-devtools)