# Migrating from ZenRows to Zyte API

Learn how to migrate from [ZenRows](https://www.zenrows.com/) to [Zyte API](../../get-started.md#zyte-api).

## Feature comparison

The following table summarizes the feature differences between both products:

| Feature                                                        | Zyte API                                                                                                                                     | ZenRows                                                          |
|----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|
| API                                                            | [HTTP](../../usage/reference.md#zapi-reference) or [proxy](../../usage/proxy-mode.md#zapi-proxy)                                             | HTTP                                                             |
| Client software                                                | [Python](https://python-zyte-api.readthedocs.io/en/stable/index.html), [Scrapy](https://scrapy-zyte-api.readthedocs.io/en/latest/index.html) | Python, NodeJS                                                   |
| Restricted website categories                                  | No broad category restrictions [^1]                                                                                                          | Banks, payment gateways, visas/permits, government               |
| [Advanced ban avoidance](../../usage/errors.md#zapi-bans)      | Always available, automatic                                                                                                                  | Only Business+, [manual](#zenrows-captcha)                       |
| [Automatic extraction](#zenrows-extract)                       | AI-powered, standard schemas                                                                                                                 | Undocumented website support, item type support or output schema |
| Markdown output                                                | No                                                                                                                                           | Yes                                                              |
| [Geolocation](../../usage/features.md#zapi-geolocation)        | 249 countries, data center support                                                                                                           | 190 countries, no data center support                            |
| [Sessions](#zenrows-sessions)                                  | Client-managed (15m) and server-managed                                                                                                      | Client-managed only (10m), no cookies                            |
| [Actions](#zenrows-actions)                                    | Basic (15), advanced, website-specific and custom                                                                                            | Basic only (10)                                                  |
| [Screenshots](#zenrows-screenshots)                            | JPEG/PNG, configurable viewport, cannot target element                                                                                       | PNG only, fixed viewport, can target element                     |
| [Network capture](../../usage/browser.md#zapi-network-capture) | Up to 5 MiB / 10 responses                                                                                                                   | Unlimited                                                        |
| Network blocking                                               | No                                                                                                                                           | Yes                                                              |
| [JavaScript disabling](../../usage/browser.md#zapi-javascript) | Yes                                                                                                                                          | No                                                               |
| Server-side CSS selectors                                      | No                                                                                                                                           | Yes                                                              |
| [Rate limiting](#zenrows-rate-limiting)                        | RPM-based                                                                                                                                    | Concurrency-based                                                |
| [Overuse handling](#zenrows-overuse)                           | Rate-limiting responses                                                                                                                      | Rate-limiting responses followed by IP blocking                  |
[^1]: Some specific websites may be blocked for legal or compliance reasons.

### Automatic extraction

ZenRows supports automatic extraction, but their documentation does not provide
details on supported websites, item types or output schemas.

[Zyte API automatic extraction](../../usage/extract/index.md#zapi-extract) is AI-based, i.e. it
works on any website of a [supported type](../../usage/extract/index.md#zapi-extract-fields) (e.g.
e-commerce, blogs/news, job postings), and we provide detailed documentation
about output schemas.

### Sessions

ZenRows only supports [client-managed sessions](../../usage/features.md#zapi-session-id), and
limits them to 10 minutes. Moreover, their sessions do not maintain cookies,
you must do that on the client side.

Zyte API allows 15 minutes for client-managed sessions, but also supports
[server-managed sessions](../../usage/features.md#zapi-session-contexts) with much longer
lifetimes and an easier API. Moreover, the [Scrapy plugin](https://scrapy-zyte-api.readthedocs.io/en/latest/index.html) supports an additional [session management API](https://scrapy-zyte-api.readthedocs.io/en/latest/usage/session.html#session).

### Screenshots

Both ZenRows and Zyte API support PNG screenshots of the visible viewport or
the full page.

ZenRows allows taking a screenshot of a specific element.

Zyte API allows configuring the browser [viewport](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/viewport).

Zyte API can return both [browserHtml](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/response/200/browserHtml) and
[screenshot](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/response/200/screenshot) on the same request, i.e. get the browser HTML
matching a given screenshot. In ZenRows you would need 2 separate requests, and
the contents of each might not be a perfect match.

### Rate limiting

ZenRows limits the number of concurrent requests that you can send, starting at
10 with the most basic plan.

Zyte API limits the number of requests per minute (RPM) that you can send. It
is 3000 by default for all Zyte API keys, but [you can request a higher
limit](../../usage/rate-limit.md#rate-limit-increase).

For services like these that support advanced features like [browser
rendering](../../usage/browser.md#zapi-browser) or [automatic extraction](../../usage/extract/index.md#zapi-extract), which usually increase response times, RPM rate limiting
allows you to maintain your throughput regardless of which features you use
thanks to unlimited concurrency, while concurrency-based limits slow down your
crawls as you use features that make requests slower.

For example, assuming an [HTTP request](../../usage/http.md#zapi-http) takes 2 seconds
and a [browser request](../../usage/browser.md#zapi-browser) takes 20 seconds, switching
from HTTP requests to browser requests with ZenRows would make your crawl 10
times slower, while Zyte API would allow you to maintain a similar crawl speed
by using more concurrent requests to make up for the response time increase.

### Overuse handling

When you exceed your concurrency with ZenRows, they start by sending
rate-limiting responses, but eventually they block your IP address for
increasing amounts of time.

With Zyte API, reaching your rate limit is not only allowed, but
[encouraged](../../usage/optimize.md#zapi-parallel), and you can [request a higher limit
limit](../../usage/rate-limit.md#rate-limit-increase) if you need it.

## Migrating

The main differences between the HTTP APIs of ZenRows and Zyte API are how
request parameters are defined and how the response is encoded.

In **ZenRows**, you send a `GET` request, and you specify parameters in the
URL query string, URL-encoded, e.g.

```bash
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZYTE_API_KEY&url=https%3A%2F%2Ftoscrape.com"
```

The API response body comes straight from the target website:

```html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Scraping Sandbox</title>
        …
```

HTTP response headers from the target website are also received as regular
headers, only prefixed with `Zr-`, and the response URL (which might not
match the request URL, e.g. in case of [redirection](../../usage/http.md#zapi-redirection)) is received as the special `Zr-Final-Url` header:

```none
Zr-Content-Encoding: br
Zr-Content-Type: text/html
Zr-Final-Url: https://toscrape.com/
```

In **Zyte API**, you send a `POST` request, and you specify parameters in the
request body as JSON, e.g.

```bash
curl \
    --user YOUR_ZYTE_API_KEY: \
    --header 'Content-Type: application/json' \
    --data '{"url": "https://toscrape.com", "httpResponseBody": true, "httpResponseHeaders": true}' \
    --compressed \
    https://api.zyte.com/v1/extract
```

The API response is a JSON object with all the response data from the target
website:

```json
{
    "url": "https://toscrape.com/",
    "statusCode": 200,
    "httpResponseBody": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KICAgIDx…",
    "httpResponseHeaders": [
        {
            "name": "content-type",
            "value": "text/html"
        },
        {
            "name": "content-encoding",
            "value": "br"
        }
    ]
}
```

> [!NOTE]
> [httpResponseBody](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/response/200/httpResponseBody) is base64-encoded to support binary
> responses, like images or PDF files.

Once you understand how to migrate a simple request like the one above, you can
migrate any other request the same way, [replacing ZenRows parameters with
Zyte API counterparts](#zenrows-params).

## Parameter mapping

| ZenRows                | Zyte API                                                                                                                                                                                                                                     |
|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| (default)              | [httpResponseBody](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/httpResponseBody), [httpResponseHeaders](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/httpResponseHeaders) |
| `apikey`               | Use [basic authentication](../../usage/reference.md#zapi-auth)                                                                                                                                                                               |
| `url`                  | [url](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/url)                                                                                                                                                     |
| `js_render`            | [browserHtml](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/browserHtml)                                                                                                                                     |
| `custom_headers`       | [customHttpRequestHeaders](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/customHttpRequestHeaders)                                                                                                           |
| `premium_proxy`        | [ipType=residential](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/ipType) (not required to [avoid bans](../../usage/errors.md#zapi-bans))                                                                   |
| `proxy_country`        | [geolocation](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/geolocation) (does not require [ipType=residential](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/ipType))       |
| `session_id`           | [session.id](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/session.id) (must be UUID4)                                                                                                                       |
| `device`               | [device](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/device)                                                                                                                                               |
| `original_status`      | N/A (see [statusCode](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/response/200/statusCode))                                                                                                                        |
| `allowed_status_codes` | N/A (see [Bad website responses](../../usage/errors.md#zapi-successful-responses-wrapping-bad-responses))                                                                                                                                    |
| `block_resources`      | Not supported                                                                                                                                                                                                                                |
| `json_response`        | See [Network capture](../../usage/browser.md#zapi-network-capture)                                                                                                                                                                           |
| `css_extractor`        | Not supported                                                                                                                                                                                                                                |
| `autoparse`            | See [Zyte API automatic extraction](../../usage/extract/index.md#zapi-extract)                                                                                                                                                               |
| `markdown_response`    | Not supported                                                                                                                                                                                                                                |
| `screenshot`           | [screenshot](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/screenshot)                                                                                                                                       |
| `screenshot_fullpage`  | [screenshotOptions.fullPage=true](https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/request/screenshotOptions.fullPage)                                                                                                  |
| `screenshot_selector`  | Not supported                                                                                                                                                                                                                                |

For parameters defining browser actions, see [Action mapping](#zenrows-actions).

## Action mapping

These are ZenRows actions and their [Zyte API counterparts](../../usage/browser.md#zapi-actions).
`check`: `click`
`click`: `click`
`evaluate`: `evaluate`
`fill`: `type`
`scroll_x`: `scrollTo`
`scroll_y`: `scrollTo`
`select_option`: `select`
`uncheck`: `click`
`wait`: `waitForTimeout`
`wait_for`: `waitForSelector`

`wait_for` only supports CSS selectors, while `waitForSelector` also
supports XPath selectors.

ZenRows also has a `solve_captcha` action that requires you to specify which
CAPTCHA you need to solve, while [Zyte API avoids bans automatically by
default](../../usage/errors.md#zapi-bans) (no action necessary), while allowing CAPTCHA management
to be disabled through [Permissions control](../../usage/features.md#zapi-permissions-control).

The following Zyte API actions are not supported by ZenRows:
`doubleClick`
`goto`
`hide`
`hover`
`keyPress`
`reload`
`scrollBottom`
`searchKeyword`
`setLocation`
`waitForNavigation`
`waitForRequest`
`waitForResponse`

Zyte API also supports [custom actions](../../ide/index.md#zapi-scripts).

ZenRows actions have `frame_`-prefixed counterparts that work on [iframes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe),
and a utility action (`frame_reveal`) to inject iframe contents into the
main DOM. On Zyte API you need to use [custom actions](../../ide/index.md#zapi-scripts)
to  interact with iframes.
