Documentation
Build secure login flows with LoginLlama's suspicious login detection API. Install it with a single agent prompt, query your analytics over MCP, or wire up the SDK by hand to detect Tor nodes, VPNs, impossible travel and new devices.
Install with one prompt
The fastest way to add LoginLlama is to let your coding agent do it. Paste the prompt below into Claude Code, Cursor, or Codex and it will install the SDK, wire up the risk check, and ask you for an API key. Prefer to do it by hand? The SDK quickstart is right below.
Integrate LoginLlama (suspicious login detection) into this project.
1. Detect the language/framework and install the official SDK:
- Better Auth (any framework): npm install better-auth-loginllama,
then add loginLlama() to plugins
- Clerk: npm install clerk-loginllama,
then export POST = loginLlamaWebhook()
from a session.created webhook route
- Auth.js (NextAuth) Credentials: npm install authjs-loginllama,
then wrap authorize in withLoginLlama()
- Node.js / Next.js / Express: npm install loginllama
- Python / Django / Flask: pip install loginllama
- PHP / Laravel: composer require joshghent/loginllama
2. Read the live API reference and examples from https://loginllama.app/docs (the machine-readable
summary is at https://loginllama.app/llms.txt).
3. Get an API key with the device flow. POST to https://loginllama.app/api/v1/oauth/device with
JSON { "client_name": "<your name>", "project_name": "<this repository's name, or
the website's name for WordPress>" }. Show me verification_uri_complete and wait
while I approve it. Poll https://loginllama.app/api/v1/oauth/token with { "grant_type":
"urn:ietf:params:oauth:grant-type:device_code", "device_code": "<device_code>" }
at the given interval until it returns access_token. If I have no account, the
approval page asks me to sign up, with 100 free checks and no card.
Save it as LOGINLLAMA_API_KEY in the environment. Never hard-code the key.
4. After my auth step succeeds, call LoginLlama with the user's identity_key,
email, IP address and user agent. Branch on the result:
- risk_score > 5 -> require MFA / extra verification or block
- otherwise -> allow the login
Always FAIL OPEN: if LoginLlama errors or times out, log it and let the user in.
5. Add a short comment linking to https://loginllama.app/docs and show me how to test it locally.LOGINLLAMA_API_KEY environment variable, never in your code.Ask your analytics (MCP)
LoginLlama ships an MCP server so your agent can query your login analytics in plain language. Connect it once with your API key, then ask questions like “what is my average login risk score for the chrome browser?”. Every tool is scoped to your team and read-only.
claude mcp add --transport http loginllama https://loginllama.app/api/v1/mcp --header "X-API-KEY: YOUR_LOGINLLAMA_API_KEY"
codex mcp add loginllama --transport http --url https://loginllama.app/api/v1/mcp --header "X-API-KEY: YOUR_LOGINLLAMA_API_KEY"
Opens Cursor and pre-fills the MCP install.
{
"mcpServers": {
"loginllama": {
"type": "http",
"url": "https://loginllama.app/api/v1/mcp",
"headers": {
"X-API-KEY": "YOUR_LOGINLLAMA_API_KEY"
}
}
}
}Endpoint: https://loginllama.app/api/v1/mcp · Transport: HTTP · Auth header: X-API-KEY: YOUR_LOGINLLAMA_API_KEY
Then just ask:
- ›“What is my average login risk score for the chrome browser?”
- ›“Which browser has the highest risk logins this month?”
- ›“Show me the 10 most recent high-risk logins.”
- ›“How many Tor or VPN logins did we see in the last 7 days?”
- ›“Break down logins by country and average risk score.”
| Tool | What it answers |
|---|---|
get_login_stats | Totals, average risk, high-risk, bot/Tor/VPN counts |
get_average_risk_score | Average risk score, filtered by browser/OS/country/device/user |
get_login_breakdown | Group logins by browser, OS, device, country, or status |
get_recent_logins | The latest login checks with risk, location and device |
get_account_usage | Remaining API credits and plan |
Quickstart: 5-Minute Integration
Get started with LoginLlama in just a few minutes. This guide will walk you through making your first API call and interpreting the results.
Get Your API Key
Your API key authenticates your requests. Keep it secure and never commit it to version control.
Make Your First Request
Send a POST request to /api/v1/login/check with the user's login information. The only required field is identity_key (user identifier).
Interpret the Response
The API returns a risk_score (0-10+) and an array of codes explaining what factors contributed to the score.
Success: Login check passed. The risk_score determines the level of suspicion (0 = safe, 10+ = highly suspicious).
Understanding Risk Scores
- 0-2: Very low risk - Safe to allow
- 3-5: Moderate risk - Consider additional verification
- 6-7: High risk - Recommend 2FA or email verification
- 8+: Very high risk - Consider blocking
Handle the Result
Based on the risk score and your security requirements, decide how to handle the login attempt. You have full control over the thresholds and actions.
Next Steps
- • Read the full API Reference to understand all available parameters
- • Explore our SDKs for easier integration
- • Check out Code Examples for common use cases
Authentication
All API requests require authentication using an API key. Include your API key in the X-API-KEY header of every request.
Authentication Header
X-API-KEY: llama_prod_xxxxxxxxxxxxxxxxxxxxllama_prod_ and score real logins. Test keys start with llama_test_ (older ones with llama_stage_), are free, and return the result you ask for.Test mode
A test key never scores a real login. It ignores the IP address, device and history, spends no credits, and picks its answer from tags you add after a + in the identity or email, like Stripe's test cards. Use it to try every branch of your code, or in an identity provider's test tenant where you can choose a user's email but not where they log in from.
| Identity | Result |
|---|---|
| [email protected] | Score 0, passes |
| [email protected] | Score 9, fails. Any score from 0 to 10 works. |
| [email protected] | That risk code, scored by its usual weight. Any risk code works. |
| [email protected] | A new device, so a new-login alert email is sent to [email protected] if she is on your team |
| user_123+new_login_location+risk_7 | Tags combine. A risk tag sets the score. |
Test responses carry meta.test: true. Test logins appear only when the dashboard is in test mode, webhooks still fire, and alert emails go only to members of your team. Test keys are limited to 60 checks a minute.
In an identity provider's test tenant
Auth0, Clerk and similar providers send LoginLlama the real IP of whoever logs in, so you cannot fake a risky location. You can choose each test user's email, and the email is enough. With Auth0:
- In a development tenant, set the LoginLlama Action's
LOGINLLAMA_API_KEYsecret to your test key. - Create a user for each branch you want to see, such as
[email protected](the Action denies the login) and[email protected](LoginLlama sends a new-login alert). - Log in as each one. Most mail providers deliver
+addresses to your normal inbox, and LoginLlama removes the tags before sending, so the alert reaches[email protected]as long as you are on the LoginLlama team.
Switch the dashboard to test mode to see those logins. Use your live key in production, since a test key looks at nothing real and protects nobody.
Security Best Practices
- •Never commit API keys to version control
- •Store keys in environment variables
- •Rotate keys periodically
- •Use test keys for development and testing
- •Manage your keys in the API settings
API Reference
Complete reference for the LoginLlama API endpoints, parameters, and responses.
Check Login Endpoint
https://loginllama.app/api/v1/login/checkAnalyzes a login attempt and returns a risk score based on IP address, device fingerprint, location, behavioral patterns, and more.
Request Parameters
Send a JSON payload with the following parameters. Only identity_key is required, but providing more data improves detection accuracy.
| Parameter | Type | Required | Description |
|---|---|---|---|
identity_key | string | Required | Unique identifier for the user (email, username, user ID, etc.) Validation: 1-255 characters Example: |
ip_address | string | Optional | IP address of the login attempt (IPv4 or IPv6) Validation: 7-45 characters Example: |
user_agent | string | Optional | Browser user agent string Validation: Maximum 1000 characters Example: |
geo_country | string | Optional | Country name or ISO code Validation: Maximum 100 characters Example: |
geo_city | string | Optional | City name Validation: Maximum 100 characters Example: |
geo_region | string | Optional | State or province, as an ISO 3166-2 subdivision code or its full name. Resolved from the IP address when omitted. Shown for countries where the subdivision is part of the address (US, Canada, Australia). Validation: Maximum 100 characters Example: |
user_time_of_day | string | Optional | User's local time in HH:mm format (24-hour) Validation: Format: HH:mm (e.g., 14:30) Example: |
email_address | string | Optional | User email address for notifications (if enabled) Validation: Valid email format, maximum 320 characters Example: |
authentication_outcome | string | Optional | Customer's authentication outcome: "success" (default), "failed", or "pending". Use "pending" for pre-auth checks, then report "success" or "failed" after authentication. Validation: One of: success, failed, pending Example: |
privileged | boolean | Optional | Set to true for admins and other accounts that would hurt most if taken over. Any risk signal on them adds privileged_account (+2), so a new network, country and device fails instead of passing, and the account gets 3 failed attempts before after_failure_burst rather than 10. Validation: true or false (default false) Example: |
timestamp | string | Optional | Timestamp of the login attempt (ISO 8601 recommended). If omitted, the server time is used. Validation: Maximum 100 characters Example: |
Request Examples
Response Format
The API returns a JSON response with the following structure:
| Parameter | Type | Required | Description |
|---|---|---|---|
data | object | Required | Primary data for the response (JSON:API spec) Example: |
data.type | string | Required | Resource type identifier Example: |
data.attributes.status | string | Required | Overall check result Example: |
data.attributes.risk_codes | string[] | Required | Array of risk codes detected Example: |
data.attributes.message | string | Required | Human-readable message Example: |
data.attributes.risk_score | number | Required | Calculated risk score (0-10+) Example: |
data.attributes.unrecognized_device | boolean | Required | Whether an unrecognized device/browser combo was detected Example: |
data.attributes.authentication_outcome | string | Required | The authentication outcome that was reported Example: |
meta | object | Required | Additional metadata about the check Example: |
meta.environment | string | Required | API key environment Example: |
meta.email_sent | boolean | Required | Whether a customer notification email was sent Example: |
Example Responses
Success: Login check passed. The risk_score determines the level of suspicion (0 = safe, 10+ = highly suspicious).
Risk Codes Reference
The codes array contains one or more of the following risk indicators:
| Code | Description | Risk |
|---|---|---|
known_tor_exit_node | IP address is a known Tor exit node | +1 |
known_vpn | IP address is from a known VPN provider | +1 |
ip_address_suspicious | IP address is in suspicious IP list (Firehol + VPN lists) | +1 |
known_proxy | IP address belongs to a cloud or hosting network: a commercial VPN, a proxy or a script | +1 |
known_botnet | IP address is in a known botnet range | +1 |
ip_address_not_used_before | This IP address has not been used by this user before | +1 |
new_login_location | Login is from a new city/country combination | +1 |
device_fingerprint_not_used_before | The device/browser combination has not been used before | +1 |
impossible_travel_detected | User traveled impossibly fast between locations | +2 |
geo_mismatch_detected | The geo_country or geo_city sent does not match where the IP address is | +1 to +2 |
known_bot | Bot detection identified this as an automated client | +1 |
behavioral_fingerprint_suspicious | Login time or pattern is unusual for this user | +1 |
after_failure_burst | More than 10 failed attempts on this account in 5 minutes, from this IP, within the last hour | +5 |
ip_password_spraying | This IP failed on more than 10 of your accounts in 10 minutes, within the last hour | +4 |
privileged_account | Sent with privileged: true, and only when another code fired. Admin accounts are worth more to an attacker, so their signals count for more | +2 |
SDKs
LoginLlama provides official SDKs for popular programming languages. SDKs handle authentication, error handling, and provide type-safe interfaces.
SDK Features
- • Type-safe interfaces with auto-completion
- • Automatic request signing and authentication
- • Built-in error handling and retries
- • Support for both async/await and promises
- • Environment variable support out of the box
Code Examples
Real-world integration examples for common frameworks and use cases.
Express.js Middleware
Add suspicious login detection to your Express.js authentication flow:
Next.js API Route
Integrate with Next.js API routes:
Next.js Login Protection with LoginLlama
Use a server action to call LoginLlama after auth succeeds, then branch on the risk score before finalizing the login.
Step-by-step integration
Create a server action for LoginLlama
Move the LoginLlama call into a server action so API keys never touch the browser.
app/actions/check-login.ts
Read IP and user agent from server headers
Use Next.js headers to capture x-forwarded-for and user-agent for higher accuracy.
Call check_login with user identity
Send identity_key + email_address + request context. Fail open if the call fails.
Invoke the action after auth succeeds
In your login page or auth callback, call the action and branch on risk_score / status.
Decide your policy
Block high-risk logins, require MFA for medium-risk, and allow low-risk logins.
Supabase Auth Integration
Integrate LoginLlama with Supabase Auth using Edge Functions, auth hooks, or server-side API routes:
Setup Steps
Create Edge Function
Run: supabase functions new check-login
Add API Key Secret
Run: supabase secrets set LOGINLLAMA_API_KEY=your_api_key_here
Deploy Function
Run: supabase functions deploy check-login
Call from Client
Invoke the function after sign-in using the Supabase client or auth hooks
Auth0 Actions Integration
Use Auth0's Post-Login Actions to check every login attempt with LoginLlama. Actions run in the authentication pipeline and can block logins, require MFA, or add custom claims to tokens.
Setup Steps
Create a New Action
In your Auth0 Dashboard, go to Actions > Library and click "Build Custom"
Name Your Action
Name it "LoginLlama Check" and select "Login / Post Login" as the trigger
Add Dependencies
Click the module icon and add "axios" as a dependency (or use native fetch in Node.js 18+)
Add Your API Key
Click the key icon to add a secret named "LOGINLLAMA_API_KEY" with your API key value
Paste the Code
Copy the code example above into the action editor
Deploy the Action
Click "Deploy" to save your action
Add to Login Flow
Go to Actions > Flows > Login and drag your "LoginLlama Check" action into the flow between "Start" and "Complete"
Apply Changes
Click "Apply" to activate the action in your login flow
What This Action Does:
- •Blocks high-risk logins (risk score ≥ 8) with descriptive error messages
- •Requires MFA for medium-risk logins (risk score ≥ 5)
- •Adds risk score to tokens as custom claims for your app to use
- •Stores risk data in user metadata for auditing
- •Fails open on errors - never blocks users if LoginLlama is unavailable
Error Handling
The API uses standard HTTP status codes and returns detailed error messages to help you debug issues.
HTTP Status Codes
| Code | Status | Description | Solution |
|---|---|---|---|
200 | Success | Request processed successfully | Check the response status field for "success" or "error" |
400 | Bad Request | Invalid request parameters (Zod validation failed) | Review request parameters and ensure they match the schema |
401 | Unauthorized | Missing or invalid API key | Check that X-API-KEY header is set with a valid API key |
402 | Payment Required | No API credits remaining | Purchase more credits or upgrade your plan |
429 | Too Many Requests | Rate limit exceeded (100 requests per minute) | Implement exponential backoff and respect X-RateLimit-Reset header |
500 | Internal Server Error | An error occurred processing your request | Check your request format and try again. Contact support if it persists |
Rate Limiting
API requests are rate limited to 100 requests per minute per API key (60 for test keys). Rate limit information is included in response headers:
X-RateLimit-Reset before retrying.Ready to Get Started?
Sign up for a free account and start protecting your users today.