How to Use cy.intercept() in Cypress
Learn to match, spy on, stub, and modify application requests with cy.intercept(), then wait for them and assert on the request and response.
cy.intercept() lets a Cypress test observe or control HTTP requests made by the application in the browser. Register the intercept before the action that triggers the request, give it an alias, and use cy.wait('@alias') to synchronize the test with the request and response. Leave the response handler out to spy on real traffic, or provide a response to stub it.
cy.intercept('GET', '/api/users').as('getUsers')
cy.visit('/users')
cy.wait('@getUsers').its('response.statusCode').should('eq', 200)
This guide covers URL matching, spying, stubbing, request modification, waits, common failures, and when to use real server responses. Examples use Cypress’s documented API; adapt paths and expected data to your application. See the Cypress cy.intercept() API reference and network requests guide.
1. Register an intercept before the request
An intercept only sees matching front-end application traffic after it is registered. Define it before cy.visit(), a click, or another action that causes the request. If the same endpoint uses multiple HTTP methods, include the method to avoid matching unintended traffic.
describe('users page', () => {
it('loads users from the API', () => {
cy.intercept('GET', '/api/users').as('getUsers')
cy.visit('/users')
cy.wait('@getUsers').then(({ request, response }) => {
expect(request.method).to.equal('GET')
expect(response.statusCode).to.equal(200)
expect(response.body).to.have.property('length')
})
})
})
The yielded interception contains the matched request and, when one is available, its response. Assert on the fields your test actually needs: for example, the request body for a form submission or the status and body for a server result.
2. Choose a matching style
cy.intercept() accepts a URL, a method and URL, or a route matcher object. Without a method, the route can match requests of any method. String patterns use minimatch with matchBase: true; URL patterns can also be regular expressions.
| Matcher | Example | Use it when |
|---|---|---|
| Exact method and path | cy.intercept('GET', '/api/users') |
The endpoint and method are stable and unambiguous. |
| Glob URL | cy.intercept('GET', '**/api/users/*') |
A host or path segment varies. |
| Regular expression | cy.intercept(/\/api\/users\?page=\d+/) |
The URL needs a pattern that is clearer as a regex. |
| RouteMatcher | cy.intercept({ method: 'GET', pathname: '/api/users' }) |
You need to constrain several request properties at once. |
A route matcher can constrain properties such as method, hostname, path, pathname, query, headers, port, https, times, and middleware. When you set multiple properties, all of them must match.
cy.intercept({
method: 'GET',
hostname: 'api.example.test',
pathname: '/v1/users',
query: { active: 'true' },
https: true,
}).as('activeUsers')
For repeated query parameters, a query matcher cannot compare all repeated values through one string. Match the URL with a regular expression or inspect all values in a handler with URLSearchParams.getAll().
cy.intercept('GET', '**/search?*', (req) => {
const url = new URL(req.url)
const tags = url.searchParams.getAll('tag')
expect(tags).to.include('cypress')
}).as('search')
3. Spy on a real response
When an intercept has no response handler, Cypress observes matching application traffic and lets the real request continue. Use this to verify the request the UI made while still exercising the actual server response.
cy.intercept('POST', '/api/orders').as('createOrder')
cy.visit('/checkout')
cy.get('[name="place-order"]').click()
cy.wait('@createOrder').then(({ request, response }) => {
expect(request.body).to.have.property('items')
expect(response.statusCode).to.equal(201)
expect(response.body).to.have.property('id')
})
Spying gives the test visibility into the real exchange, but it also makes the test depend on the server and its data. Use it when server integration is part of what the test needs to cover.
4. Stub a response
Provide a static response when the test needs controlled data or needs to cover a case that is difficult to produce from the real server. A static response may be a string, a body object, a fixture, or a StaticResponse with options such as status, headers, body, delay, throttling, or a forced network error.
cy.intercept('GET', '/api/users', {
statusCode: 200,
body: [{ id: 7, name: 'Ari' }],
headers: { 'content-type': 'application/json' },
}).as('getUsers')
cy.visit('/users')
cy.wait('@getUsers')
cy.contains('Ari').should('be.visible')
To reuse a fixture file, pass its path in a static response:
cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers')
Use a dynamic handler when the response depends on the incoming request. Call req.reply() with the response you want the application to receive.
cy.intercept('POST', '/api/search', (req) => {
const term = req.body.term
req.reply({
statusCode: 200,
body: { results: term ? [{ id: 1, title: `Result for ${term}` }] : [] },
})
}).as('search')
Stubs make response data predictable, but they do not check that the server would return the same response and do not exercise that server endpoint. Cypress recommends mixing stubbed tests with real end-to-end coverage where useful. See its discussion of spying and stubbing tradeoffs.
5. Inspect or modify a real exchange
Use a route handler to inspect or change request properties, then let the request continue to the real server. req.continue() sends it upstream and can take a callback for examining the real response. Calling req.reply() or req.continue() ends propagation to later matching handlers.
cy.intercept('POST', '/api/orders', (req) => {
expect(req.body).to.have.property('items')
req.headers['x-test-run'] = 'checkout'
req.continue((res) => {
expect(res.statusCode).to.equal(201)
})
}).as('createOrder')
Choose one outcome deliberately: reply with a controlled response for a stub, or continue to the server for a real exchange. A request handler can also inspect fields without ending propagation, allowing the request to proceed normally.
6. Wait for requests and assert on them
Alias an intercept with .as('name'), then wait on that alias. The wait synchronizes on the matching request and response cycle and yields its interception. This is more targeted than sleeping for a guessed amount of time because the test waits for the network event it needs.
cy.intercept('GET', '/api/profile').as('profile')
cy.visit('/account')
cy.wait('@profile').then((interception) => {
expect(interception.request.url).to.include('/api/profile')
expect(interception.response.statusCode).to.equal(200)
expect(interception.response.body.email).to.match(/@/)
})
For a test that needs multiple requests, pass an array of aliases to cy.wait():
cy.intercept('GET', '/api/profile').as('profile')
cy.intercept('GET', '/api/preferences').as('preferences')
cy.visit('/account')
cy.wait(['@profile', '@preferences']).then((interceptions) => {
expect(interceptions).to.have.length(2)
})
Each alias must match the intended request. If a page issues several calls to the same route, an alias wait can match successive requests; structure assertions around the particular request your test expects.
7. Understand scope, cleanup, and route order
- Browser application traffic:
cy.intercept()observes requests made by the front-end application. Cypress states, “Cypress only intercepts requests made by your front-end application.” See the API reference. cy.request()is different: it is sent from Cypress’s Node process, so it is not browser application traffic observed bycy.intercept(). See the Cypress FAQ.- Per-test lifecycle: Cypress clears intercept routes before each test. Register the routes in every test that needs them, commonly inside that test or a setup hook.
- Overlapping routes: ordinary route handlers are generally processed in reverse definition order. Routes with
middleware: truerun first. Check the Routes display in the Cypress Command Log when investigating which handler matched.
Native network interception behavior has changed across Cypress versions. The official guide notes that before Cypress 16, application requests used the legacy network path. Check the native network interception guide against the Cypress version in your project when behavior differs.
8. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
cy.wait('@alias') times out |
The route was registered after the request, or the method or URL did not match. | Register before the triggering action. Inspect the actual request URL and method, then refine the matcher. |
| The request appears in the app but the intercept does not fire | The traffic may come from cy.request(), which runs in Cypress’s Node process, not the front-end. |
Use an application action that makes the browser request, or assert on the cy.request() result directly. |
| A broad intercept catches the wrong request | No method was specified, or a glob matches more paths than intended. | Add the HTTP method and constrain the hostname, pathname, or query using a RouteMatcher. |
| The expected response is not returned | The intercept is only spying, or a different overlapping route handler matched first. | Provide a static response or call req.reply(). Review route order and middleware behavior in the Routes display. |
| The test works alone but fails in a suite | The route may be missing in another test because Cypress clears intercepts between tests. | Register the intercept in each test or the setup hook that runs for each test. |
| A repeated query value is not matched as expected | A query matcher string does not compare all repeated values. | Use a URL regex or inspect new URL(req.url).searchParams.getAll('key') in a handler. |
| Assertions see no response | The request may fail before receiving a response, for example when using a forced network error. | Assert the expected failure condition and account for the response being absent. |
9. Performance, reliability, and test cost
Intercepts add a synchronization point tied to a request rather than an arbitrary delay. Stubbing can make a test independent of server availability and data setup, while spying on real responses covers more of the application-to-server path but depends on that service and its state. Choose based on the behavior under test, and keep some end-to-end coverage that exercises real server responses.
Match routes narrowly so unrelated requests do not satisfy a wait or receive a stub. Keep response fixtures representative of the fields the UI consumes, and include failure and edge-case stubs where the application needs to render those states. Cypress’s network guide describes stubs as a way to make data controlled and recommends balancing them with real server coverage; it does not establish a universal timing or cost benchmark.
10. Or skip the browser setup
If your goal is to capture a page image or PDF for review, documentation, or an automated workflow, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. It is separate from Cypress: it captures a page, while cy.intercept() observes or controls application network requests in Cypress tests.
ScreenshotNeo accepts cookie and consent banners like a visitor, then removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. An MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents using Claude, Cursor, or another MCP client.
For example, save a WebP screenshot of a page with cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options and configuration. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for free and get 1,000 screenshots a month with no card.
11. FAQ
Does cy.intercept() make the request itself?
No. It matches traffic from the front-end application. Use an application action to trigger that traffic; cy.request() runs from Cypress’s Node process.
Do I need to stub every request?
No. An intercept without a response handler spies on the real request. Stub where controlled data or a hard-to-trigger case is useful, and retain real server coverage where integration matters.
Can one intercept match only a limited number of requests?
Yes. A RouteMatcher supports times to limit how many times a route matches. Consult the API reference for details and version-specific behavior.
Where can I see which routes Cypress registered?
Inspect the Routes display in the Cypress Command Log while debugging the test.


