Skip to content

API

This page demonstrates various ways to integrate with xhr.dev.

Don't see your codebase/stack? Message us!

TLS Options

xhr.dev man-in-the-middle's (MITM) requests/responses to get past bot challenges. You can use either TLS mode below.

Option 1: Quickstart (disable SSL checks inline)

Use this for fast local testing only.

bash
curl --proxy https://magic.xhr.dev \
     -k \
     -H "x-xhr-api-key: ${XHR_API_KEY}" \
     https://app.example.com/login
javascript
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
python
requests.get('https://app.example.com/login', verify=False)
bash
curl -s https://docs.xhr.dev/xhrdev.pem -o xhrdev.pem

Install and trust xhrdev.pem in your OS trust store:

macOS

bash
sudo security add-trusted-cert \
  -d \
  -r trustRoot \
  -k /Library/Keychains/System.keychain \
  ./xhrdev.pem

Linux (Debian/Ubuntu)

bash
sudo cp ./xhrdev.pem /usr/local/share/ca-certificates/xhrdev.crt
sudo update-ca-certificates

Linux (RHEL/CentOS/Fedora)

bash
sudo cp ./xhrdev.pem /etc/pki/ca-trust/source/anchors/xhrdev.crt
sudo update-ca-trust extract

Windows (PowerShell as Administrator)

powershell
Import-Certificate -FilePath .\xhrdev.pem -CertStoreLocation Cert:\LocalMachine\Root

Restart your terminal/app/browser after installing the certificate.

bash
curl --proxy https://magic.xhr.dev \
     --cacert xhrdev.pem \
     -H "x-xhr-api-key: ${XHR_API_KEY}" \
     https://app.example.com/login

Custom Headers and Cookies

Headers you pass in are passed to the target server. All x-xhr-* headers are removed before sent to the origin server.

Control headers

Control headers change the behaviour of how the proxy operates

Required control headers:

  • x-xhr-api-key: Your API key

Optional control headers:

  • x-xhr-managed-proxy: true - use xhr.dev's managed proxy. default: none (no third-party proxy provided)
  • x-xhr-customer-managed-proxy: [url] - bring your own proxy. default: none (no third-party proxy provided)
managed proxy

x-xhr-managed-proxy: true - use xhr.dev's managed proxy

proxy

x-xhr-customer-managed-proxy: [url] Bring your own proxy. Pass in your proxy URL. Will be validated before use. Cannot be combined with x-xhr-managed-proxy.

Node.js

Choose one:

  • Quickstart: set process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'.
  • Recommended: load and trust xhrdev.pem (examples below).

Node.js with Fetch

typescript
import { readFileSync } from 'node:fs';
import { fetch, ProxyAgent } from 'undici';

const proxyUrl = 'https://magic.xhr.dev';
const url = 'https://app.example.com/login';
const ca = readFileSync('./xhrdev.pem');

// native fetch requires n@18+
const response = await fetch(url, {
  dispatcher: new ProxyAgent({
    uri: proxyUrl,
    requestTls: { ca },
  }),
  headers: {
    'x-xhr-api-key': process.env.XHR_API_KEY,
  },
  redirect: 'manual',
});

console.log({ response });

Node.js with Axios popular

typescript
import axios from 'axios';
import { readFileSync } from 'node:fs';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { createCookieAgent } from 'http-cookie-agent/http';
import { CookieJar } from 'tough-cookie';

const HttpsProxyCookieAgent = createCookieAgent(HttpsProxyAgent);

const ca = readFileSync('./xhrdev.pem');
const jar = new CookieJar();
const httpsAgent = new HttpsProxyCookieAgent('https://magic.xhr.dev', {
  cookies: { jar },
});
httpsAgent.options.ca = ca;

const { data } = await axios.get('https://app.example.com/login', {
  httpsAgent,
  headers: { 'x-xhr-api-key': process.env.XHR_API_KEY },
});

console.log({ data });

Python popular

python
import os
import requests

xhr_api_key = os.getenv('XHR_API_KEY')
headers = {
  "x-xhr-api-key": xhr_api_key,
}
proxy_url = 'https://magic.xhr.dev'
proxies = {
  'http': proxy_url,
  'https': proxy_url,
}

session = requests.Session()
verify = 'xhrdev.pem'  # quickstart option: False

response = session.get(
  'https://core.cro.ie',
  headers=headers,
  proxies=proxies,
  verify=verify
)

print('Response:', response.text)

Playwright popular

Recommended: install xhrdev.pem into your OS/browser trust store. Quickstart: set ignoreHTTPSErrors: true.

typescript
import { chromium } from 'playwright';

const url = 'https://news.ycombinator.com';
const xhrApiKey = process.env.XHR_API_KEY;
const proxyUrl = 'https://magic.xhr.dev';

const browser = await chromium.launch({
  proxy: {
    server: proxyUrl,
  },
});
const context = await browser.newContext({
  ignoreHTTPSErrors: false, // quickstart option: true
  extraHTTPHeaders: {
    'x-xhr-api-key': xhrApiKey,
  },
});
const page = await context.newPage();

try {
  await page.goto(url);

  const title = await page.title();
  const content = await page.content();

  console.log({ content, title });
} finally {
  await browser.close();
}

Puppeteer popular

Recommended: install xhrdev.pem into your OS/browser trust store. Quickstart: set acceptInsecureCerts: true.

typescript
import puppeteer from 'puppeteer';

const url = 'https://news.ycombinator.com';
const xhrApiKey = process.env.XHR_API_KEY;
const proxyUrl = 'https://magic.xhr.dev';

const browser = await puppeteer.launch({
  acceptInsecureCerts: false, // quickstart option: true
  args: [`--proxy-server=${proxyUrl}`],
});
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
  'x-xhr-api-key': xhrApiKey,
});

try {
  await page.goto(url);

  const title = await page.title();
  const content = await page.content();

  console.log({ content, title });
} finally {
  await browser.close();
}

Go

go
package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io"
    "log"
    "net/http"
    "net/http/cookiejar"
    "net/url"
    "os"
)

func main() {
    apiKey := os.Getenv("XHR_API_KEY")
    if apiKey == "" {
        log.Fatal("API key is not defined. Please set the 'XHR_API_KEY' environment variable.")
    }

    proxyURL, err := url.Parse("https://magic.xhr.dev")
    if err != nil {
        log.Fatal(err)
    }

    caCert, err := os.ReadFile("xhrdev.pem")
    if err != nil {
        log.Fatal(err)
    }

    certPool := x509.NewCertPool()
    if ok := certPool.AppendCertsFromPEM(caCert); !ok {
        log.Fatal("failed to append xhrdev.pem to cert pool")
    }

    jar, err := cookiejar.New(nil)
    if err != nil {
        log.Fatal(err)
    }

    client := &http.Client{
        Transport: &http.Transport{
            Proxy: http.ProxyURL(proxyURL),
            TLSClientConfig: &tls.Config{
                RootCAs: certPool,
                InsecureSkipVerify: false, // quickstart option: true
            },
        },
        Jar: jar,
    }

    req, err := http.NewRequest("POST", "https://app.example.com/login", nil)
    if err != nil {
        log.Fatal(err)
    }

    req.Header.Set("x-xhr-api-key", apiKey)

    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Response:", string(body))
}

Ruby

ruby
require 'net/http'
require 'uri'
require 'http/cookie_jar'
require 'openssl'

api_key = ENV['XHR_API_KEY']
abort("API key is not defined. Please set the 'XHR_API_KEY' environment variable.") if api_key.nil? || api_key.empty?

proxy_uri = URI.parse('https://magic.xhr.dev')
uri = URI.parse('https://app.example.com/login')

jar = HTTP::CookieJar.new
http = Net::HTTP.new(uri.host, uri.port, proxy_uri.host, proxy_uri.port, nil, nil)
http.use_ssl = true
http.ca_file = 'xhrdev.pem'
http.verify_mode = OpenSSL::SSL::VERIFY_PEER # quickstart option: OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(uri.request_uri)
request['x-xhr-api-key'] = api_key

cookies = jar.cookies(uri)
request['Cookie'] = cookies.map { |c| "#{c.name}=#{c.value}" }.join('; ') unless cookies.empty?

response = http.request(request)

jar.parse(response['Set-Cookie'], uri) if response['Set-Cookie']

puts "Response: #{response.body}"