Back to Blog

How to Set Up a Proxy for GraphQL API: IP Rotation and Bypassing Rate Limiting with Code Examples

A complete guide to setting up a proxy for working with GraphQL API: code examples, IP address rotation, bypassing rate limiting, and protection against bans.

📅February 15, 2026
```html

GraphQL APIs are becoming increasingly popular, but with that come restrictions: rate limiting, IP blocks, geographic filters. If you are working with large volumes of data through GraphQL—scraping e-commerce platforms, gathering analytics from social media, or testing APIs—you cannot do without proxies. In this article, we will discuss how to properly set up a proxy for GraphQL requests, implement IP rotation, and avoid blocks.

We will show practical examples in Python and Node.js, discuss common mistakes, and provide recommendations on choosing the type of proxy for different tasks.

Why Proxies are Needed for GraphQL Requests

GraphQL APIs are often used to retrieve large amounts of data in a short time. Unlike REST APIs, where data is split across multiple endpoints, GraphQL allows you to request everything you need in one query. This is convenient but creates problems:

  • Rate limiting — most public GraphQL APIs limit the number of requests from a single IP (for example, GitHub API: 5000 requests per hour, Shopify: 2 requests per second)
  • IP blocks — exceeding limits or suspicious activity can result in your IP being blocked for several hours or permanently
  • Geographic restrictions — some APIs are only available from certain countries (for example, local marketplaces or regional services)
  • Anti-scraping protection — servers monitor request patterns and block suspicious IPs

Proxies solve these problems by allowing you to distribute requests across multiple IP addresses, simulate requests from different regions, and bypass blocks. This is especially important when working with:

  • Data scraping from e-commerce platforms (Shopify, WooCommerce GraphQL API)
  • Gathering analytics from social media (Facebook Graph API, Instagram API)
  • Monitoring prices and product availability
  • Testing APIs from different geographic locations
  • Automating data collection for analytics and research

Which Type of Proxy to Choose for Working with GraphQL

The choice of proxy type depends on the task and API requirements. Let's discuss three main types and their application for GraphQL requests:

Proxy Type Speed Anonymity When to Use
Datacenter Proxies Very High (10-50 ms) Medium Scraping public APIs, testing, high speed is more important than anonymity
Residential Proxies Medium (100-300 ms) Very High Working with protected APIs (Shopify, Facebook), bypassing strict filters
Mobile Proxies Medium (150-400 ms) Maximum Instagram API, TikTok API, mobile applications with GraphQL

Recommendations for Selection:

  • For Public APIs (GitHub, OpenWeather) — datacenter proxies are sufficient, they are fast and inexpensive
  • For E-commerce (Shopify, WooCommerce) — residential proxies, as these platforms actively filter datacenters
  • For Social Media (Facebook Graph API, Instagram) — mobile or residential proxies are mandatory
  • For Mass Scraping — a combination: datacenters for main traffic + residential for rotation during blocks

Setting Up Proxies in Python for GraphQL (requests, httpx, gql)

Python is one of the most popular languages for working with APIs. Let's look at three ways to set up proxies for GraphQL requests.

Option 1: Requests Library (Simple HTTP Client)

The simplest way is to use the standard requests library. It is suitable for basic GraphQL requests without complex logic.

import requests
import json

# Setting up the proxy
proxies = {
    'http': 'http://username:password@proxy.example.com:8080',
    'https': 'http://username:password@proxy.example.com:8080'
}

# GraphQL request
query = """
query {
  products(first: 10) {
    edges {
      node {
        id
        title
        priceRange {
          minVariantPrice {
            amount
          }
        }
      }
    }
  }
}
"""

# Sending the request through the proxy
url = "https://your-shop.myshopify.com/api/2024-01/graphql.json"
headers = {
    'Content-Type': 'application/json',
    'X-Shopify-Storefront-Access-Token': 'your_token_here',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}

response = requests.post(
    url,
    json={'query': query},
    headers=headers,
    proxies=proxies,
    timeout=30
)

data = response.json()
print(json.dumps(data, indent=2))

Option 2: httpx Library (Asynchronous Requests)

If you need to send many requests in parallel, use httpx with async/await support:

import httpx
import asyncio
import json

async def fetch_graphql(query, proxy_url):
    url = "https://api.example.com/graphql"
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_TOKEN',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'
    }
    
    # Setting up the proxy for httpx
    proxies = {
        "http://": proxy_url,
        "https://": proxy_url
    }
    
    async with httpx.AsyncClient(proxies=proxies, timeout=30.0) as client:
        response = await client.post(
            url,
            json={'query': query},
            headers=headers
        )
        return response.json()

# Usage
query = """
query {
  viewer {
    login
    repositories(first: 5) {
      nodes {
        name
        stargazerCount
      }
    }
  }
}
"""

proxy = "http://user:pass@proxy.example.com:8080"
result = asyncio.run(fetch_graphql(query, proxy))
print(json.dumps(result, indent=2))

Option 3: gql Library (Specialized GraphQL Client)

For advanced work with GraphQL, use the gql library—it supports schema validation, caching, and convenient request handling:

from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport

# Setting up transport with proxy
transport = RequestsHTTPTransport(
    url='https://api.example.com/graphql',
    headers={
        'Authorization': 'Bearer YOUR_TOKEN',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)'
    },
    proxies={
        'http': 'http://user:pass@proxy.example.com:8080',
        'https': 'http://user:pass@proxy.example.com:8080'
    },
    timeout=30
)

# Creating the client
client = Client(transport=transport, fetch_schema_from_transport=True)

# GraphQL request
query = gql("""
    query GetProducts($first: Int!) {
        products(first: $first) {
            edges {
                node {
                    id
                    title
                    variants(first: 1) {
                        edges {
                            node {
                                price
                            }
                        }
                    }
                }
            }
        }
    }
""")

# Executing the request
result = client.execute(query, variable_values={"first": 20})
print(result)

Setting Up Proxies in Node.js for GraphQL (axios, apollo-client)

Node.js is also widely used for working with GraphQL APIs. Let's look at two main approaches.

Option 1: Axios with Proxy

A simple and flexible HTTP client with proxy support:

const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');

// Setting up the proxy
const proxyUrl = 'http://username:password@proxy.example.com:8080';
const httpsAgent = new HttpsProxyAgent(proxyUrl);

// GraphQL request
const query = `
  query {
    products(first: 10) {
      edges {
        node {
          id
          title
          priceRange {
            minVariantPrice {
              amount
            }
          }
        }
      }
    }
  }
`;

// Sending the request
axios.post('https://your-shop.myshopify.com/api/2024-01/graphql.json', 
  { query },
  {
    headers: {
      'Content-Type': 'application/json',
      'X-Shopify-Storefront-Access-Token': 'your_token_here',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
    },
    httpsAgent: httpsAgent,
    timeout: 30000
  }
)
.then(response => {
  console.log(JSON.stringify(response.data, null, 2));
})
.catch(error => {
  console.error('Error:', error.message);
});

Option 2: Apollo Client with Proxy

Apollo Client is the most popular GraphQL client for Node.js and the browser. Setting up a proxy through a custom fetch:

const { ApolloClient, InMemoryCache, HttpLink, gql } = require('@apollo/client');
const fetch = require('cross-fetch');
const HttpsProxyAgent = require('https-proxy-agent');

// Proxy agent
const proxyUrl = 'http://user:pass@proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);

// Custom fetch with proxy
const customFetch = (uri, options) => {
  return fetch(uri, {
    ...options,
    agent: agent
  });
};

// Creating Apollo Client
const client = new ApolloClient({
  link: new HttpLink({
    uri: 'https://api.example.com/graphql',
    fetch: customFetch,
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'
    }
  }),
  cache: new InMemoryCache()
});

// GraphQL request
const GET_REPOS = gql`
  query GetRepositories($login: String!) {
    user(login: $login) {
      repositories(first: 5) {
        nodes {
          name
          stargazerCount
        }
      }
    }
  }
`;

// Executing the request
client.query({
  query: GET_REPOS,
  variables: { login: 'facebook' }
})
.then(result => {
  console.log(JSON.stringify(result.data, null, 2));
})
.catch(error => {
  console.error('Error:', error);
});

Implementing Proxy Rotation to Bypass Rate Limiting

Proxy rotation is a key technique for bypassing API restrictions. Instead of sending all requests from one IP, you distribute them among many proxies. This allows you to bypass rate limiting and avoid blocks.

Simple Rotation in Python

A basic implementation of rotation with cyclic switching of proxies:

import requests
import itertools
import time

# List of proxies
PROXY_LIST = [
    'http://user:pass@proxy1.example.com:8080',
    'http://user:pass@proxy2.example.com:8080',
    'http://user:pass@proxy3.example.com:8080',
    'http://user:pass@proxy4.example.com:8080',
]

# Create an infinite iterator
proxy_pool = itertools.cycle(PROXY_LIST)

def make_graphql_request(query):
    """Send a GraphQL request with proxy rotation"""
    proxy = next(proxy_pool)
    proxies = {'http': proxy, 'https': proxy}
    
    url = "https://api.example.com/graphql"
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer YOUR_TOKEN',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
    }
    
    try:
        response = requests.post(
            url,
            json={'query': query},
            headers=headers,
            proxies=proxies,
            timeout=30
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error with proxy {proxy}: {e}")
        # Switch to the next proxy
        return make_graphql_request(query)

# Example usage
queries = [
    'query { products(first: 10) { edges { node { id title } } } }',
    'query { collections(first: 5) { edges { node { id title } } } }',
    'query { shop { name email } }'
]

for query in queries:
    result = make_graphql_request(query)
    print(result)
    time.sleep(1)  # Pause between requests

Smart Rotation with Error Tracking

A more advanced version that tracks non-working proxies and automatically excludes them from the pool:

import requests
import random
from collections import defaultdict
import time

class ProxyRotator:
    def __init__(self, proxy_list, max_failures=3):
        self.proxy_list = proxy_list.copy()
        self.max_failures = max_failures
        self.failures = defaultdict(int)
        self.active_proxies = proxy_list.copy()
    
    def get_proxy(self):
        """Get a random active proxy"""
        if not self.active_proxies:
            raise Exception("All proxies are unavailable!")
        return random.choice(self.active_proxies)
    
    def mark_failure(self, proxy):
        """Mark a failed attempt"""
        self.failures[proxy] += 1
        if self.failures[proxy] >= self.max_failures:
            print(f"Proxy {proxy} excluded from the pool (failure limit exceeded)")
            if proxy in self.active_proxies:
                self.active_proxies.remove(proxy)
    
    def mark_success(self, proxy):
        """Reset the failure counter on success"""
        self.failures[proxy] = 0

# Initialization
proxies = [
    'http://user:pass@proxy1.example.com:8080',
    'http://user:pass@proxy2.example.com:8080',
    'http://user:pass@proxy3.example.com:8080',
]

rotator = ProxyRotator(proxies)

def graphql_request_with_retry(query, max_retries=3):
    """GraphQL request with automatic retries"""
    for attempt in range(max_retries):
        proxy = rotator.get_proxy()
        proxies_dict = {'http': proxy, 'https': proxy}
        
        try:
            response = requests.post(
                'https://api.example.com/graphql',
                json={'query': query},
                headers={
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer TOKEN',
                    'User-Agent': 'Mozilla/5.0'
                },
                proxies=proxies_dict,
                timeout=30
            )
            response.raise_for_status()
            
            # Success — reset the failure counter
            rotator.mark_success(proxy)
            return response.json()
            
        except Exception as e:
            print(f"Attempt {attempt + 1}/{max_retries} with {proxy} failed: {e}")
            rotator.mark_failure(proxy)
            time.sleep(2)  # Pause before retry
    
    raise Exception("Failed to execute the request after all attempts")

# Usage
query = 'query { products(first: 10) { edges { node { id title } } } }'
result = graphql_request_with_retry(query)
print(result)

Setting Up Headers and User-Agent for GraphQL Requests

Correct HTTP headers are critically important for successful interaction with GraphQL APIs through proxies. Many APIs check not only the IP but also the request headers.

Mandatory Headers for GraphQL

headers = {
    # Content type — always application/json for GraphQL
    'Content-Type': 'application/json',
    
    # Authorization (depends on the API)
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    # or
    'X-Shopify-Storefront-Access-Token': 'token_here',
    
    # User-Agent — simulating a real browser
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    
    # Accept — indicating that we accept JSON
    'Accept': 'application/json',
    
    # Accept-Language — user language
    'Accept-Language': 'en-US,en;q=0.9',
    
    # Accept-Encoding — support for compression
    'Accept-Encoding': 'gzip, deflate, br',
    
    # Referer — where the request came from (optional)
    'Referer': 'https://example.com/',
    
    # Origin — for CORS requests
    'Origin': 'https://example.com'
}

User-Agent Rotation

For greater anonymity, it is recommended to rotate the User-Agent along with the proxies:

import random

USER_AGENTS = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15'
]

def get_random_headers(token):
    """Generate random headers"""
    return {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {token}',
        'User-Agent': random.choice(USER_AGENTS),
        'Accept': 'application/json',
        'Accept-Language': 'en-US,en;q=0.9',
        'Accept-Encoding': 'gzip, deflate, br'
    }

Error Handling and Retries through Proxies

When working with proxies, errors are inevitable: timeouts, unavailable proxies, blocks. It is important to properly handle these situations and implement a retry mechanism.

Common Errors with GraphQL through Proxies

  • Timeouts — the proxy is slow or overloaded (increase timeout to 30-60 seconds)
  • HTTP 407 Proxy Authentication Required — incorrect proxy username/password
  • HTTP 429 Too Many Requests — rate limit exceeded (proxy rotation is needed)
  • HTTP 403 Forbidden — proxy IP is blocked (switch to residential proxies)
  • Connection refused — proxy is unavailable (exclude from the pool)

Advanced Error Handling

import requests
import time
from requests.exceptions import ProxyError, Timeout, ConnectionError

def graphql_request_robust(query, proxy, max_retries=3, backoff=2):
    """
    Reliable GraphQL request with handling of all types of errors
    
    Args:
        query: GraphQL query
        proxy: Proxy URL
        max_retries: maximum attempts
        backoff: multiplier for delay between attempts
    """
    url = "https://api.example.com/graphql"
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer TOKEN',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
    }
    proxies = {'http': proxy, 'https': proxy}
    
    for attempt in range(max_retries):
        try:
            response = requests.post(
                url,
                json={'query': query},
                headers=headers,
                proxies=proxies,
                timeout=30
            )
            
            # Check for rate limiting
            if response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 60))
                print(f"Rate limit! Waiting for {retry_after} seconds...")
                time.sleep(retry_after)
                continue
            
            # Check for IP block
            if response.status_code == 403:
                print(f"IP {proxy} is blocked! Need another proxy.")
                raise Exception("IP blocked")
            
            # Check for proxy authorization errors
            if response.status_code == 407:
                print(f"Proxy authorization error {proxy}")
                raise Exception("Proxy auth failed")
            
            response.raise_for_status()
            
            # Check for GraphQL errors
            data = response.json()
            if 'errors' in data:
                print(f"GraphQL errors: {data['errors']}")
                # Some errors can be retried, some cannot
                if is_retryable_graphql_error(data['errors']):
                    time.sleep(backoff * (attempt + 1))
                    continue
                else:
                    raise Exception(f"GraphQL error: {data['errors']}")
            
            return data
            
        except (ProxyError, ConnectionError) as e:
            print(f"Attempt {attempt + 1}: Proxy unavailable - {e}")
            time.sleep(backoff * (attempt + 1))
            
        except Timeout as e:
            print(f"Attempt {attempt + 1}: Timeout - {e}")
            time.sleep(backoff * (attempt + 1))
            
        except requests.exceptions.HTTPError as e:
            print(f"Attempt {attempt + 1}: HTTP error - {e}")
            if attempt < max_retries - 1:
                time.sleep(backoff * (attempt + 1))
            else:
                raise
    
    raise Exception(f"Failed to execute request after {max_retries} attempts")

def is_retryable_graphql_error(errors):
    """Determines if the request can be retried on a GraphQL error"""
    retryable_codes = ['THROTTLED', 'INTERNAL_ERROR', 'TIMEOUT']
    for error in errors:
        if error.get('extensions', {}).get('code') in retryable_codes:
            return True
    return False

Best Practices for Working with GraphQL through Proxies

Let's summarize and provide recommendations for effective work with GraphQL APIs through proxies:

✓ Query Optimization

  • Request only the necessary fields—GraphQL allows you to specify exactly what you need
  • Use pagination instead of requesting all data at once
  • Group related queries into one (GraphQL supports multiple queries)
  • Cache results on the client side to reduce the number of requests

✓ Proxy Management

  • Use a pool of at least 5-10 proxies for rotation
  • Regularly check the functionality of proxies (health check)
  • Automatically exclude non-working proxies from the pool
  • For critical tasks, keep backup proxies of another type

✓ Compliance with Limits

  • Study the API documentation—exact limits are specified there
  • Add delays between requests (at least 1-2 seconds)
  • Monitor the X-RateLimit-Remaining and X-RateLimit-Reset headers
  • When receiving a 429 error, increase the delay exponentially

✓ Security and Anonymity

  • Always use HTTPS proxies to protect authorization tokens
  • Rotate User-Agent along with proxies
  • Do not store tokens in code—use environment variables
  • Log only the minimally necessary information

Recommended Architecture for Large Projects

If you are working with large volumes of data, we recommend the following architecture:

  1. Task Queue (Redis, RabbitMQ) — for distributing requests among workers
  2. Worker Pool — each worker uses its own proxy
  3. Proxy Manager — monitors the state of proxies and distributes them among workers
  4. Database — for storing results and task states
  5. Monitoring — tracking errors, speed, proxy usage

Conclusion

Working with GraphQL APIs through proxies is not just about adding the proxies parameter to the request. For reliable and effective operation, you need to implement proxy rotation, proper error handling, set up headers, and comply with API limits. We have reviewed practical examples in Python and Node.js that you can immediately use in your projects.

Key takeaways: use residential proxies for protected APIs (Shopify, Facebook), datacenters for public APIs and mass scraping, implement automatic rotation with exclusion of non-working proxies, add delays, and handle all types of errors. This will allow you to work stably with any GraphQL APIs without blocks.

If you plan to work with GraphQL APIs in production, we recommend using residential proxies — they provide maximum stability and minimal risk of blocks. For testing and development, datacenter proxies will suffice—they are faster and cheaper.

```