Proxies for Custom GPT Actions (formerly ChatGPT Plugins)
Heads up: OpenAI deprecated ChatGPT Plugins in April 2024 and replaced them with Custom GPT Actions. Actions use the same OpenAPI spec model as plugins did, but are configured per-GPT in the GPT Builder rather than installed from a Plugin Store. The proxy-layer architecture below works identically for either; the rest of this guide uses Actions terminology.
Custom GPT Actions extend ChatGPT's capabilities by letting a GPT call external APIs and fetch real-time data through your OpenAPI-described endpoints. When those external sources enforce geographic restrictions, rate limits, or IP-based access controls, proxy infrastructure becomes essential for reliable Action operation.
Why GPT Actions Need Proxies
Action backends run server-side, typically on a single cloud provider's IP range. This creates three problems:
- Geo-Restrictions: Data sources that serve different content by region (e.g., pricing, product availability, local news) always see the same datacenter IP.
- Rate Limits: Multiple GPT users sharing the same server IP exhaust upstream rate limits quickly.
- IP Blocking: Some APIs block known cloud provider IP ranges entirely.
Action Architecture with Proxy Layer
User → ChatGPT → Custom GPT Action → Hex Proxies → External API
↓
ISP or Residential IP
(appears as real user)Python Action Server with Proxy
import os
import httpx
from fastapi import FastAPI
from dataclasses import dataclassapp = FastAPI()
@dataclass(frozen=True) class ProxySettings: base_url: str = os.environ.get("HEX_PROXY_HOST", "http://gate.hexproxies.com:8080") username: str = os.environ["HEX_PROXY_USER"] password: str = os.environ["HEX_PROXY_PASS"]
@property def url(self) -> str: return f"http://{self.username}:{self.password}@gate.hexproxies.com:8080"
PROXY = ProxySettings()
@app.get("/api/fetch-prices") async def fetch_prices(query: str, country: str = "US"): """Action endpoint that fetches price data through geo-targeted proxy.""" proxy_url = ( f"http://{PROXY.username}-country-{country.lower()}:" f"{PROXY.password}@gate.hexproxies.com:8080" ) async with httpx.AsyncClient(proxy=proxy_url, timeout=30) as client: resp = await client.get( f"https://api.example.com/prices?q={query}", headers={"Accept": "application/json"}, ) resp.raise_for_status() return {"prices": resp.json(), "region": country} ```
The OpenAPI spec for this Action is what you paste into the GPT Builder's "Actions → Schema" panel; ChatGPT calls the endpoint over HTTPS with your configured auth (API key, OAuth, or service account).
Node.js Action Server with Proxy
const express = require('express');
const { HttpsProxyAgent } = require('https-proxy-agent');const app = express();
function createProxyAgent(country = '') {
const baseUser = process.env.HEX_PROXY_USER;
const user = country ? ${baseUser}-country-${country.toLowerCase()} : baseUser;
const proxyUrl = http://${user}:${process.env.HEX_PROXY_PASS}@gate.hexproxies.com:8080;
return new HttpsProxyAgent(proxyUrl);
}
app.get('/api/search', async (req, res) => { const { query, region = 'US' } = req.query; const agent = createProxyAgent(region);
try {
const response = await fetch(https://api.example.com/search?q=${query}, {
agent,
headers: { 'Accept': 'application/json' },
});
const data = await response.json();
res.json({ results: data, region });
} catch (error) {
res.status(502).json({ error: 'Upstream request failed' });
}
});
app.listen(3000); ```
Session Management for Multi-Step Actions
Some Actions need to maintain state across multiple API calls (e.g., login, then fetch data). Use sticky sessions to keep the same IP:
def get_sticky_proxy(session_id: str) -> str:
user = os.environ["HEX_PROXY_USER"]
pwd = os.environ["HEX_PROXY_PASS"]
return f"http://{user}-session-{session_id}:{pwd}@gate.hexproxies.com:8080"Rate Limit Distribution
When multiple ChatGPT users invoke your Action simultaneously, each request goes through a different proxy IP. This distributes rate limit consumption across Hex Proxies' IP pool rather than concentrating it on your server's single IP.
Caching Layer
Add a Redis or in-memory cache between your Action and the proxy layer. Many Action queries are repetitive — caching responses for 5-15 minutes dramatically reduces proxy usage and improves response times:
from functools import lru_cache
from datetime import datetime@lru_cache(maxsize=1000) def cached_fetch(url: str, cache_key: str) -> dict: """Cache responses by URL and time-bucketed key.""" # Implementation fetches through proxy on cache miss pass ```
Deployment Considerations
Deploy your Action server in a region close to Hex Proxies infrastructure (US East) for minimal latency. Our ISP proxies in Ashburn, VA deliver sub-50ms latency — adding your Action server in the same region keeps total round-trip time under 100ms.