New API documentation
As of Oct 2021 readthedocs.io version Paxful API documentation is deprecated, please instead use new version of documentation available at developers portal. The new documentation reflects many new endpoints that we have added recently, also it includes more detailed description of available filter parameters and response bodies. Hope you will enjoy it!
Authentication section below is still relevant and its snippets of bootstrapping code still can be used, they are scheduled to be migrated to developers portal as well.
Authentication
For authentication, you need to generate your API key under your Paxful account settings in developer tab
Example of request content
apikey=c3KJ7M5qw2SKSrw3QnQA8D2DHZCpwTlx&nonce=1455035029943&offer_hash=Agq1Bpw7oX9&apiseal=9b67236984c40a7c66892782cc3b0426833e699a1b44e1a5b44d63d82ba75767
In which following parameters are required:
- apikey - Your key generated under your Paxful account settings, unique per user
- nonce - current unix timestamp (required to protect against replay attacks)
- apiseal - signature (digest) of the request params passed through HMAC-SHA256 construct
To generate the apiseal, you need to pass the request payload (i.e. apikey, nonce + request parameters) through hash method using the secret key provided from the UI
Examples
OpenSSL:
echo -n “apikey=c3KJ7M5qw2SKSrw3QnQA8D2DHZCpwTlx&nonce=1455035029943&offer_hash=Agq1Bpw7oX9” | openssl dgst -sha256 -hmac f8KjbW13VxrY1ziOF5j48Kd9OYxSmleT
(stdin)= 9b67236984c40a7c66892782cc3b0426833e699a1b44e1a5b44d63d82ba75767
PHP: minimal working example
// Payload which is sent to server
$payload = [
'apikey' => 'YOURAPIKEY',
'nonce' => time(),
];
// Generation of apiseal
// Please note the PHP_QUERY_RFC3986 enc_type
$apiseal = hash_hmac('sha256', http_build_query($payload, null, '&', PHP_QUERY_RFC3986), 'YOURSECRET');
// Append the generated apiseal to payload
$payload['apiseal'] = $apiseal;
// Set request URL (in this case we check your balance)
$ch = curl_init('https://paxful.com/api/wallet/balance');
// NOTICE that we send the payload as a string instead of POST parameters
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload, null, '&', PHP_QUERY_RFC3986));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Accept: application/json; version=1',
'Content-Type: text/plain',
]);
// fetch response
$response = curl_exec($ch);
// convert json response into array
$data = json_decode($response);
var_dump($data);
curl_close($ch);
Javascript:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js"></script>
<script>
var xhr = new XMLHttpRequest(),
secret = 'aefij3ldaase_ase23fdAdwjnA2123fFa',
body = 'apikey=' + 'dgsdrij234fsdfgkhr' + '&nonce=' + Date.now() + '&offer_hash=Agq1Bpw7oX9&margin=50';
var seal = CryptoJS.HmacSHA256(body, secret);
xhr.open('POST', 'https://www.paxful.com/api/offer/list');
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.setRequestHeader('Accept', 'application/json');
xhr.send(body + '&apiseal=' + seal);
</script>
Python 3:
import hmac
import time
from hashlib import sha256
from urllib.parse import urlencode
import requests # pip install requests
API_URL = "https://paxful.com/api/offer/list"
API_KEY = "<your api key>"
API_SECRET = "<your api secret>"
nonce = int(time.time())
payload = {"apikey": API_KEY, "nonce": nonce}
payload = urlencode(sorted(payload.items()))
apiseal = hmac.new(API_SECRET.encode(), payload.encode(), sha256).hexdigest()
data_with_apiseal = payload + "&apiseal=" + apiseal
headers = {"Accept": "application/json", "Content-Type": "text/plain"}
resp = requests.post(API_URL, data=data_with_apiseal, headers=headers)
Python 2.7:
import time
import hmac
import requests
import urllib
from hashlib import sha256
nonce = int(time.time())
url = 'https://paxful.com/api/offer/list'
payload = {'apikey':pax_key, 'nonce':nonce}
payload = urllib.urlencode(sorted(payload.items()))
apiseal = hmac.new(pax_secret, payload, sha256).hexdigest()
data_with_apiseal = payload + '&apiseal=' + apiseal
headers = {'Accept': 'application/json', 'Content-Type': 'text/plain'}
resp = requests.post(url, data = data_with_apiseal, headers=headers)
C#
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Security.Cryptography;
// Here is the method that generates hmac
class HmacGenerate
{
public string GenerateHMAC(string secret, string payload)
{
var keyBytes = Encoding.UTF8.GetBytes(secret);
var hmac = new HMACSHA256 { Key = keyBytes };
var rawSig = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
return BitConverter.ToString(rawSig).Replace("-", string.Empty).ToLower();
}
}
//Here we make the request
public class WebRequestPost
{
public static void Main()
{
HmacGenerate hmac = new HmacGenerate();
//Generate today's date in the format of dd.MM.yyyy
DateTime dateAndTime = DateTime.Now;
var date = dateAndTime.ToString("dd.MM.yyyy");
//Variables apiKey, secret are used for hmac generation, you need to use your secret and api key from user settings
var apiKey = "yourApiKey";
var secret = "yourApiSecret";
//Variables offerHash, margin are used for this example request, those variables should contain your information
var offerHash = "2zEeNVVADeb";
var margin = 50;
//Variables body and theKey is used for request body, theKey holds generate hmac
var body = "apikey=" + apiKey + "&nonce=" + date + "&offer_hash=" + offerHash +"&margin=" + margin;
var theKey = "&apiseal="+hmac.GenerateHMAC(secret,body);
// Create a request using a URL that can receive a post.
WebRequest request = WebRequest.Create("https://paxful.com/api/offer/list");
// Set the Method property of the request to POST.
request.Method = "POST";
// Create POST data and convert it to a byte array.
string postData = body + theKey;
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Set the ContentType property of the WebRequest.
request.ContentType = "application/text";
// Set the ContentLength property of the WebRequest.
request.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream();
// Write the data to the request stream.
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close();
// Get the response.
WebResponse response = request.GetResponse();
// Display the status.
Console.WriteLine(((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
Console.WriteLine(responseFromServer);
// Clean up the streams.
reader.Close();
dataStream.Close();
response.Close();
}
}
Golang
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/antonholmquist/jason"
)
func Paxful() {
paxfulAPIKey := "<your api key>"
paxfulSecretAPIKey := "<your secret api key>"
nonce := fmt.Sprintf("%d", time.Now().Unix())
values := url.Values{}
values.Add("apikey", paxfulAPIKey)
values.Add("nonce", nonce)
payload := values.Encode()
mac := hmac.New(sha256.New, []byte(paxfulSecretAPIKey))
mac.Write([]byte(payload))
apiseal := hex.EncodeToString(mac.Sum(nil))
values.Add("apiseal", apiseal)
endpoint := "wallet/balance"
url := "https://paxful.com/api/" + endpoint
req, err := http.NewRequest(
http.MethodPost, url,
strings.NewReader(values.Encode()),
)
if err != nil {
panic(err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "text/plain")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
v, err := jason.NewObjectFromReader(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(v.String())
}
Request and response formats
Valid Request headers MUST contain
Accept: application/json; version=1
Content-Type: text/plain
Currently there's only version=1 supported. When a new version becomes available, if no "version" is set, it will use the latest one. For compatibility you should use the version header appended to the Accept Header
Payload should be encoded correctly using RFC3986. For php use http_build_query() an for for Python use urlencode().
Response formats are always in json containing data status timestamp and data or error (in case of errors)
Example of a successful response
{
"status": "success",
"timestamp": 1455032576,
"data": {
"success": true,
"offer_hash": "Agq1Bpw7oX9"
}
}
Example of error response
{
"status": "error",
"timestamp": 1455032576,
"error": {
"code": 404,
"message": "Not Found"
}
}
All Responses are expected to have status code 200
Response also contains two custom header
X-RateLimit-Limit
X-RateLimit-Remaining
The X-RateLimit-Limit shows your overall limit, while the X-RateLimit-Remaining shows how many requests you're allowed to make until you hit your hour limit.
Testing correctness of API seal
Some libraries are generating HMAC incorrectly or have multiple ways of doing it. We have example tester that you can compare your generated API seal and API seal generated by our correct example code.
https://paxful.com/api_test.htm