Base URL
https://api.crytify.com
Authentication
Send your API key on every API request using the Bearer token format. This works the same way regardless of HTTP method — /v1/ip and /v1/trust are POST, while /v1/usage and /v1/me are GET.
curl -X POST "https://api.crytify.com/v1/trust" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer cry_live_xxxxxxxxxxxx" \
-d '{ ... }'
IP Lookup
Use /v1/ip when you only need network intelligence. It costs 1 credit and does not return a trust decision.
curl -X POST "https://api.crytify.com/v1/ip" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer cry_live_xxxxxxxxxxxx" \
-d '{
"ip": "8.8.8.8"
}'
{
"request_id": "req_123",
"ip": "8.8.8.8",
"network": {
"country": "US",
"network_type": "business"
},
"credits": {
"charged": 1,
"remaining_today": 999
}
}
BIN Lookup
Free · No API key required
Look up card network, type, issuing bank, and country from the first 6–8 digits of any payment card (BIN/IIN). No authentication needed — rate limited to 10 requests per minute per IP.
curl "https://api.crytify.com/api/bin/453201"
{
"bin": "453201",
"scheme": "Visa",
"type": "Debit",
"brand": "Visa Classic",
"prepaid": false,
"bank": {
"name": "Jyske Bank",
"city": "Silkeborg",
"phone": "+4589893300"
},
"country": {
"name": "Denmark",
"code": "DK",
"emoji": "🇩🇰",
"currency": "DKK"
}
}
| Field | Description |
| bin |
The BIN you queried (6–8 digits). |
| scheme |
Card network: Visa, Mastercard, Amex, etc. |
| type |
Credit, Debit, or Prepaid. |
| brand |
Sub-brand (e.g. Visa Classic, Mastercard Gold). |
| prepaid |
true if the card is prepaid. |
| bank.name |
Issuing bank name. |
| country.code |
ISO 3166-1 alpha-2 country code. |
| country.currency |
ISO 4217 currency code. |
Returns HTTP 404 if BIN is not in the database. Returns HTTP 429 when rate limit is exceeded — back off and retry after 60 seconds.
Trust Decision
Use /v1/trust before rendering protected content. Send IP, method, path, and sanitized browser headers. Optionally include a fingerprint object from the JavaScript SDK to improve request consistency analysis.
curl -X POST "https://api.crytify.com/v1/trust" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer cry_live_xxxxxxxxxxxx" \
-d '{
"ip": "203.0.113.5",
"mode": "hard",
"method": "POST",
"path": "/login",
"url": "https://app.example.com/login",
"challenge_return_url": "https://app.example.com/login",
"headers": {
"user-agent": "Mozilla/5.0 ...",
"accept": "text/html,application/xhtml+xml",
"accept-language": "en-US,en;q=0.9",
"sec-ch-ua-platform": "\"Windows\""
},
"fingerprint": {
"client_context": "generated by the Crytify JavaScript SDK"
}
}'
{
"request_id": "req_456",
"decision": "CHALLENGE",
"mode": "hard",
"trust_score": 42,
"risk_score": 61,
"reasons": [
"identity_risk"
],
"challenge": {
"type": "hosted",
"url": "https://crytify.com/challenge/abc123",
"expires_in": 600,
"token_param": "crytify_challenge_token"
},
"network": {
"country": "US",
"network_type": "unknown"
},
"credits": {
"charged": 8,
"remaining_today": 992
}
}
Hosted challenge flow
When the decision is CHALLENGE, redirect the visitor to challenge.url.
After verification, Crytify redirects back to challenge_return_url with crytify_challenge_token.
Send that value as challenge_token in the next /v1/trust call.
Usage
GET · Free — does not consume credits
Check remaining daily credits and plan limits for the authenticated key's account. Useful for showing usage in your own dashboard or alerting before you run out.
curl "https://api.crytify.com/v1/usage" \
-H "Authorization: Bearer cry_live_xxxxxxxxxxxx"
{
"user_id": 7,
"plan": "free",
"credits": {
"daily_limit": 1000,
"daily_used": 42,
"daily_remaining": 958,
"unlimited": false,
"balance": 0,
"reset_at": "2026-07-04T00:00:00Z"
}
}
Me
GET · Free — does not consume credits
Returns metadata about the API key being used and the account's current plan. Useful for verifying which key/mode is active without checking the dashboard.
curl "https://api.crytify.com/v1/me" \
-H "Authorization: Bearer cry_live_xxxxxxxxxxxx"
{
"api_key": {
"id": 22,
"name": "Default Key",
"prefix": "cry_live_b6ba5ef81c422782",
"default_mode": "medium",
"can_override_mode": false,
"status": "active",
"created_at": "2026-07-03 01:05:42"
},
"account": {
"id": 7,
"plan": "free"
}
}
Modes and Credits
| Mode |
Credits |
Use case |
| easy | 2 | Low-friction content pages. |
| medium | 4 | Default mode for forms, account pages, and general protection. |
| hard | 8 | Login, signup, checkout, admin, and API protection. |
Error Codes
| Code | HTTP | Meaning |
| invalid_json |
400 |
Request body is not valid JSON. |
| missing_required_field |
400 |
A required field such as ip is missing. |
| invalid_ip |
400 |
The submitted IP address is invalid. |
| invalid_api_key |
401 |
API key is missing or invalid. |
| insufficient_credits |
402 |
Wallet does not have enough credits. |
| rate_limited |
429 |
Too many requests. |
| internal_error |
500 |
Internal error. Charged credits are refunded. |
Enforcement Example
Your application decides what to do with the returned decision. A typical pre-render gate looks like this:
if ($decision === 'BLOCK') {
http_response_code(403);
exit('Blocked');
}
if ($decision === 'CHALLENGE') {
header('Location: ' . $result['challenge']['url']);
exit;
}
if (!empty($_GET['crytify_challenge_token'])) {
// Send this token in the next /v1/trust call as challenge_token.
}
renderProtectedPage();
Page Protection
SDK · Two-layer gate
The Crytify SDK lets you hide page content from bots before it is ever rendered or visible.
There are two complementary layers — use them together for maximum coverage.
1
Server-side gate (PHP)
Evaluate the visitor IP and headers in your PHP code before rendering anything. Blocks all bots — including those that don't execute JavaScript (curl, Python requests, Scrapy, wget).
2
Client-side gate (JS)
A script in <head> hides the page immediately, collects consistency metadata, and only reveals content after the trust check passes.
Browser → Your server → Crytify /v1/trust
↓ PHP gate (before render)
Browser → crytify-protect.js → Your endpoint → Crytify /v1/trust
↓ JS gate (before visible)
JavaScript SDK
crytify-protect.js is a drop-in script that hides your page immediately on load, collects a browser fingerprint, and sends it to your backend. Content is only shown after your backend confirms the visitor is allowed.
1. Embed in <head> — before any other scripts
<head>
<!-- Must be the first script in <head> -->
<script src="/crytify-protect.js"
data-endpoint="/api/crytify-check"
data-block-url="/blocked"
data-timeout="5000">
</script>
</head>
Options
| Attribute |
Required |
Description |
| data-endpoint |
Yes |
URL of your backend trust-check endpoint. The JS will POST the fingerprint here. |
| data-block-url |
No |
Redirect here when the visitor is blocked. Defaults to an inline block message. |
| data-timeout |
No |
Milliseconds before failing open (showing content anyway) if your endpoint doesn't respond. Default: 5000. |
| data-debug |
No |
Set to "true" to print decision logs to the browser console. |
2. Create the backend endpoint (data-endpoint)
The JS POSTs { "fingerprint": { ... } } to your endpoint. Your endpoint calls Crytify and returns { "allow": true } or { "allow": false }.
<?php
// /api/crytify-check.php — the endpoint crytify-protect.js calls
require 'CrytifyClient.php';
header('Content-Type: application/json');
$body = json_decode(file_get_contents('php://input'), true);
$fingerprint = $body['fingerprint'] ?? null;
$crytify = new CrytifyClient('cry_live_xxxxxxxxxxxx');
$result = $crytify->trust(
CrytifyClient::resolveIp(),
getallheaders(),
['fingerprint' => $fingerprint]
);
echo json_encode(['allow' => $result->isAllow(), 'reason' => $result->reason()]);
Fail-open safety: If your endpoint is unreachable or takes longer than data-timeout milliseconds, the script automatically reveals the page. This prevents a Crytify outage from breaking your site for real users.
PHP SDK
CrytifyClient.php is a self-contained PHP class with no dependencies. Drop it anywhere in your project.
Use it as middleware to gate page rendering before a single byte of HTML is sent.
Download
# Copy the file into your project
cp crytify-sdk/CrytifyClient.php your-project/lib/CrytifyClient.php
Basic usage — server-side gate
Place this at the very top of any PHP page you want to protect, before any output.
<?php
require 'CrytifyClient.php';
$crytify = new CrytifyClient('cry_live_xxxxxxxxxxxx', mode: 'medium');
$result = $crytify->trust(
CrytifyClient::resolveIp(), // resolves CF-Connecting-IP / X-Forwarded-For automatically
getallheaders(),
['path' => $_SERVER['REQUEST_URI'], 'method' => $_SERVER['REQUEST_METHOD']]
);
if ($result->isBlock()) {
http_response_code(403);
include 'blocked.html';
exit;
}
if ($result->isChallenge()) {
header('Location: ' . $result->challengeUrl());
exit;
}
// Visitor passed — render your page normally
CrytifyResult methods
| Method | Returns | Description |
| isAllow() |
bool |
true when the visitor passed. |
| isChallenge() |
bool |
true when additional verification is recommended. |
| isBlock() |
bool |
true when the visitor should be denied. |
| challengeUrl() |
?string |
Hosted Crytify challenge URL when the decision is CHALLENGE. |
| decision() |
string |
Raw decision string: ALLOW, CHALLENGE, or BLOCK. |
| trustScore() |
int |
0–100. Higher is more trusted. |
| riskScore() |
int |
0–100. Higher is more suspicious. |
| reason() |
string |
Human-readable reason string from the decision. |
| isFailOpen() |
bool |
true when a network error caused automatic fail-open. |
CodeIgniter 4 Filter
For CI4 projects, use the provided filter class to protect routes declaratively.
// app/Config/Filters.php
public array $aliases = [
'crytify' => \App\Filters\CrytifyGateFilter::class,
];
// app/Config/Routes.php — protect individual routes
$routes->get('checkout', 'OrderController::checkout', ['filter' => 'crytify']);
// Or protect an entire group
$routes->group('members', ['filter' => 'crytify'], function ($routes) {
$routes->get('dashboard', 'MemberController::dashboard');
$routes->get('profile', 'MemberController::profile');
});
Browser Fingerprint
Passing a fingerprint object in /v1/trust adds client-side consistency context that is not available from server headers alone.
Exact signal handling is private and public responses only expose broad reason categories.
Public categories
| Signal | What it means | Confidence |
| fingerprint_risk |
Client-side consistency context contributed to the decision. |
public category |
| automation_risk |
Automation-related context contributed to the decision. |
public category |
| identity_risk |
Session or identity consistency context contributed to the decision. |
public category |
Collect fingerprint with the JS helper
crytify-fingerprint.js collects all signals and returns a plain object. Use it independently if you want to collect the fingerprint yourself and pass it to your server without the automatic page-hiding behavior.
<script src="/crytify-fingerprint.js"></script>
<script>
crytifyFingerprint().then(function (fp) {
// fp is a plain JS object — send it to your server
fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: document.getElementById('email').value,
password: document.getElementById('password').value,
fingerprint: fp, // <-- include fingerprint in your form submission
}),
});
});
</script>
Your server then passes fingerprint directly to /v1/trust in the request body. No new Crytify endpoint is needed.