The Shift4 API is REST-based, providing a complete payment processing platform for developers. It has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
All API requests are made to the following base URL:
https://api.shift4.comKey characteristics of the API:
- All requests must use HTTPS. Any HTTP requests will be rejected.
- Request bodies use form-encoded parameters.
- All responses are returned as JSON.
- The API operates in two modes: test and live, each with separate API keys. Test mode transactions are completely isolated from production.
All API requests must be authenticated using your API secret key via HTTP Basic Auth. Use your secret key as the username and leave the password empty. Never share your secret key or expose it in client-side code.
You can find your API keys in the Shift4 Dashboard.
The Shift4 API uses HTTP Basic Authentication. To authenticate, provide your API secret key as the username and leave the password field empty.
- Test keys start with
sk_test_ - Live keys start with
sk_live_
Example request:
curl https://api.shift4.com/charges -u pr_test_tXHm9qV9qV9bjIRHcQr9PLPa:The key above belongs to a shared demo account, so the examples throughout these docs are runnable as written. Substitute your own key to work against your data. The demo account predates the current prefixes, so its keys read pr_test_ and pu_test_ where yours will read sk_test_ and pk_test_.
Note the trailing colon (:) after the key -- this tells cURL to send an empty password.
Important security considerations:
- Never expose your secret key in client-side code, public repositories, or anywhere accessible to end users.
- For client-side integrations with Shift4.js, use your public keys instead (
pk_test_for test mode,pk_live_for live mode).
The Shift4 API uses standard HTTP status codes to indicate the outcome of a request:
| Status Code | Meaning |
|---|---|
200 | Success -- The request completed successfully. |
400 | Bad Request -- The request was malformed or missing required parameters. |
401 | Unauthorized -- No valid API key was provided. |
402 | Card Error -- The card was declined or failed verification. |
404 | Not Found -- The requested resource does not exist. |
429 | Rate Limit -- Too many requests in a given time period. |
500 | Server Error -- An internal error occurred on Shift4's servers. |
Error object structure:
When an error occurs, the response body contains an error object with the following fields:
{
"error": {
"type": "card_error",
"code": "invalid_number",
"message": "The card number is not a valid credit card number."
}
}Error types:
| Type | Description |
|---|---|
invalid_request | The request was malformed or missing required parameters. |
card_error | The card was declined or failed verification. |
gateway_error | An internal error occurred on Shift4's servers. |
Card error codes:
| Type | Description |
|---|---|
invalid_request | The request was malformed or missing required parameters. |
card_error | The card was declined or failed verification. |
gateway_error | An internal error occurred on Shift4's servers. |
| Code | Description |
|---|---|
invalid_number | The card number is not a valid credit card number. |
invalid_expiry_month | The card's expiration month is invalid. |
invalid_expiry_year | The card's expiration year is invalid. |
invalid_cvc | The card's CVC number is invalid. |
incorrect_cvc | The card's CVC is incorrect. |
incorrect_zip | The card's ZIP code failed validation. |
card_declined | The card was declined. |
expired_card | The card has expired. |
insufficient_funds | The card has insufficient funds to complete the purchase. |
lost_or_stolen | The card has been reported as lost or stolen. |
suspected_fraud | The charge was flagged as potentially fraudulent. |
processing_error | An error occurred while processing the card. |
blacklisted | The card number, fingerprint, or email is on a blacklist. |
expired_token | The token has already been used or has expired. |
limit_exceeded | The charge amount exceeds the maximum allowed. |
authentication_required | The charge requires 3D Secure authentication. |
do_not_honor | The issuing bank has declined the transaction. |
do_not_try_again | The issuing bank has declined and requests no further attempts. |
The Shift4 API supports idempotent requests, allowing you to safely retry requests without the risk of performing the same operation twice. This is especially important for payment operations.
To make an idempotent request, include the Idempotency-Key header with a unique value (UUIDs are recommended):
curl https://api.shift4.com/charges \
-u pr_test_tXHm9qV9qV9bjIRHcQr9PLPa: \
-H "Idempotency-Key: unique_key_123" \
-d "amount=1000" \
-d "currency=USD" \
-d "card=tok_example"Key details:
- Idempotency keys are valid for 24 hours.
- If a duplicate request is detected, the server responds with the original response and includes an
Idempotent-Replayed: trueheader. - Only
POSTrequests support idempotency keys.GETandDELETErequests are inherently idempotent.
Most Shift4 objects support a metadata parameter, allowing you to attach custom key-value pairs to resources.
- Up to 20 key-value pairs per object.
- Keys can be up to 40 characters long.
- Values can be up to 500 characters long.
- Useful for storing reference IDs from your own system (e.g., order IDs, user IDs).
Example:
curl https://api.shift4.com/charges \
-u pr_test_tXHm9qV9qV9bjIRHcQr9PLPa: \
-d "amount=1000" \
-d "currency=USD" \
-d "card=tok_example" \
-d "metadata[order_id]=12345" \
-d "metadata[customer_email]=user@example.com"Metadata is not used by Shift4 for any processing -- it is purely for your own record-keeping.
All list endpoints return paginated results using cursor-based pagination.
Query parameters:
| Parameter | Description |
|---|---|
limit | Number of results per page (1--100, default 10). |
startingAfterId | Return results starting after the object with this ID. |
endingBeforeId | Return results ending before the object with this ID. |
created[gte] | Filter results created at or after this timestamp. |
created[lte] | Filter results created at or before this timestamp. |
Example response:
{
"list": [
{ "id": "char_7Hd8sK2mNpQ4rLbVx0TfYcWs", "amount": 1000, "currency": "USD" },
{ "id": "char_9x2Kd8bViQ0rLmNpQ4TsHfYc", "amount": 2500, "currency": "USD" }
],
"hasMore": true
}When hasMore is true, pass the id of the last item in the list array as the startingAfterId parameter to retrieve the next page.