Learn how to access x402-protected content on Pinata.
Overview
When attempting to access x402-protected content without a payment payload, the gateway returns payment requirements. After making a payment, requesters receive a payment proof that grants access to the content.
The Payment Flow
The content creator provides their dedicated Pinata gateway URL. Replace your-gateway.mypinata.cloud in examples with the actual gateway domain provided.
Step 1: Request the Content
Make a GET request to the x402 gateway URL:
curl https://your-gateway.mypinata.cloud/x402/cid/bafkreih...
Step 2: Receive Payment Requirements (402 Response)
The gateway returns HTTP 402 with payment details:
{
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "base",
"maxAmountRequired": "10000",
"resource": "https://your-gateway.mypinata.cloud/x402/cid/bafkreih...",
"description": "Access fee",
"mimeType": "application/json",
"payTo": "0x6135561038E7C676473431842e586C8248276AED",
"maxTimeoutSeconds": 60,
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"extra": {
"name": "USD Coin",
"version": "2"
}
}
],
"error": "Provide a valid X-Payment header to access this content"
}
Key Fields:
network: Blockchain network (e.g., base)
asset: Token contract address (USDC on Base)
payTo: Recipient wallet address (content creator receives payment here)
maxAmountRequired: Payment amount in smallest unit (e.g., 10000 = 0.01 USDC)
Step 3: Access Content with Payment Proof
After successful payment, include the X-Payment header in the request:
curl https://your-gateway.mypinata.cloud/x402/cid/bafkreih... \
-H "X-Payment: eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3..."
The gateway will:
- Validate the
X-Payment header
- Verify the payment proof
- Check that amount, recipient, and network match
- Serve the private content
Successful Response:
HTTP/200 OK
Content-Type: application/json
{
"your": "content"
}
Using x402 Libraries
The x402 protocol has client libraries that automate the payment flow. First, set up your wallet:
Setting Up Your Wallet
The x402 libraries require a Viem account or Coinbase Developer Platform wallet:
Option 1: Viem Local Account
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
Option 2: Coinbase CDP Wallet
import { Coinbase, Wallet } from "@coinbase/coinbase-sdk";
const coinbase = new Coinbase({
apiKeyName: "YOUR_API_KEY_NAME",
privateKey: "YOUR_PRIVATE_KEY",
});
const wallet = await Wallet.create();
const account = await wallet.getDefaultAddress();
Using the Libraries
Once you have your wallet set up, use the x402 libraries to access paid content:
x402-fetch
import { wrapFetchWithPayment } from "x402-fetch";
import { privateKeyToAccount } from "viem/accounts";
// Set up your wallet
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const fetchWithPayment = wrapFetchWithPayment(fetch, account);
// Automatically handles 402 response and payment
const response = await fetchWithPayment(
"https://your-gateway.mypinata.cloud/x402/cid/bafkreih..."
);
const content = await response.json();
console.log(content);
x402-axios
import { wrapAxiosWithPayment } from "x402-axios";
import axios from "axios";
import { privateKeyToAccount } from "viem/accounts";
// Set up your wallet
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const axiosWithPayment = wrapAxiosWithPayment(axios, account);
// Automatically handles 402 response and payment
const response = await axiosWithPayment.get(
"https://your-gateway.mypinata.cloud/x402/cid/bafkreih..."
);
console.log(response.data);
Technical Details (Optional)
Pinata uses Coinbase Facilitator to process x402 payments, which provides access to Coinbase’s Discovery Bazaar network for broader content discovery.
Understanding Payment Amounts
USDC uses 6 decimals, so amounts use the token’s smallest unit:
| USD Amount | maxAmountRequired | Calculation |
| $0.01 | 10000 | $0.01 × 1,000,000 |
| $0.10 | 100000 | $0.10 × 1,000,000 |
| $1.00 | 1000000 | $1.00 × 1,000,000 |
| $10.00 | 10000000 | $10.00 × 1,000,000 |
Formula: USD Amount × 1,000,000 = token amount
Error Handling
402 Payment Required
Payment has not been made yet. Follow the payment flow above.
403 Forbidden
Payment proof is invalid or expired. Make a new payment.
404 Not Found
The CID doesn’t exist or isn’t attached to a payment instruction.
500 Internal Server Error
Gateway or facilitator error. Contact the content creator.
Network Support
| Network | Status | Token | Use Case |
| Base (Mainnet) | ✅ Available | USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) | Production |
| Base Sepolia (Testnet) | ✅ Available | USDC (0x036CbD53842c5426634e7929541eC2318f3dCF7e) | Testing |
Best Practices
- Handle 402 responses gracefully: Display payment requirements clearly
- Use the x402 libraries: They handle the complex payment flow automatically
- Test on Base Sepolia first: Verify integration before using mainnet
- Store payment proofs: If accessing content multiple times (check expiry)
- Monitor USDC balance: Ensure sufficient funds for payments
Example: Complete Integration
import { wrapFetchWithPayment } from "x402-fetch";
import { privateKeyToAccount } from "viem/accounts";
// Set up your wallet
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const fetchWithPayment = wrapFetchWithPayment(fetch, account);
// Access paid content
async function accessPaidContent(cid: string) {
try {
const url = `https://your-gateway.mypinata.cloud/x402/cid/${cid}`;
const response = await fetchWithPayment(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const content = await response.json();
console.log("Content accessed successfully:", content);
return content;
} catch (error) {
console.error("Failed to access content:", error);
throw error;
}
}
// Usage
accessPaidContent("bafkreih5aznjvttude6c3wbvqeebb6rlx5wkbzyppv7garjiubll2ceym4");
Resources
Support
Need help accessing paid content? Contact the content creator or reach out to [email protected].