QuickStart Guide
Get started with the BoloDoc API in minutes. This guide walks you through authentication and making your first API calls.
Your API key is required to authenticate all API requests. You received it via email when you registered.
How to Access Your API Key:
- Check your registration email - Your API key was sent when you signed up
- Reset it - Go to your Profile page and use the "Reset API Key" button
Exchange your API key for a JWT access token. Tokens expire after 60 minutes.
Request:
POST https://www.bolodoc.io/v1/auth/token
Content-Type: application/json
{
"api_key": "your-api-key-here"
}
Code Examples:
// Using fetch API
const API_KEY = process.env.BOLO_API_KEY; // Store securely
const BASE_URL = "https://www.bolodoc.io";
async function getAccessToken() {
const response = await fetch(`${BASE_URL}/v1/auth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
api_key: API_KEY
})
});
if (response.ok) {
const data = await response.json();
console.log('Access token:', data.access_token);
console.log('Expires in:', data.expires_in, 'seconds');
console.log('Your role:', data.role);
return data.access_token;
} else {
const error = await response.text();
throw new Error(`Failed to get token: ${response.status} - ${error}`);
}
}
// Usage
getAccessToken()
.then(token => console.log('Ready to make API calls!'))
.catch(error => console.error(error));
import os
import requests
# Store API key securely in environment variable
API_KEY = os.getenv("BOLO_API_KEY")
BASE_URL = "https://www.bolodoc.io"
def get_access_token():
response = requests.post(
f"{BASE_URL}/v1/auth/token",
json={"api_key": API_KEY}
)
if response.status_code == 200:
token_data = response.json()
print(f"Access token: {token_data['access_token']}")
print(f"Expires in: {token_data['expires_in']} seconds")
print(f"Your role: {token_data['role']}")
return token_data['access_token']
else:
raise Exception(f"Failed to get token: {response.status_code} - {response.text}")
# Usage
try:
access_token = get_access_token()
print("Ready to make API calls!")
except Exception as e:
print(f"Error: {e}")
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class BoloAuth {
private static final String API_KEY = System.getenv("BOLO_API_KEY");
private static final String BASE_URL = "https://www.bolodoc.io";
private static final HttpClient client = HttpClient.newHttpClient();
private static final Gson gson = new Gson();
public static String getAccessToken() throws Exception {
JsonObject requestBody = new JsonObject();
requestBody.addProperty("api_key", API_KEY);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/v1/auth/token"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(gson.toJson(requestBody)))
.build();
HttpResponse response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
JsonObject data = gson.fromJson(response.body(), JsonObject.class);
String accessToken = data.get("access_token").getAsString();
int expiresIn = data.get("expires_in").getAsInt();
String role = data.get("role").getAsString();
System.out.println("Access token: " + accessToken);
System.out.println("Expires in: " + expiresIn + " seconds");
System.out.println("Your role: " + role);
return accessToken;
} else {
throw new Exception("Failed to get token: " +
response.statusCode() + " - " + response.body());
}
}
public static void main(String[] args) {
try {
String token = getAccessToken();
System.out.println("Ready to make API calls!");
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class BoloAuth
{
private static readonly string ApiKey = Environment.GetEnvironmentVariable("BOLO_API_KEY");
private static readonly string BaseUrl = "https://www.bolodoc.io";
private static readonly HttpClient client = new HttpClient();
public static async Task GetAccessToken()
{
var requestBody = new { api_key = ApiKey };
var json = JsonSerializer.Serialize(requestBody);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{BaseUrl}/v1/auth/token", content);
if (response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize(responseBody);
var accessToken = data.GetProperty("access_token").GetString();
var expiresIn = data.GetProperty("expires_in").GetInt32();
var role = data.GetProperty("role").GetString();
Console.WriteLine($"Access token: {accessToken}");
Console.WriteLine($"Expires in: {expiresIn} seconds");
Console.WriteLine($"Your role: {role}");
return accessToken;
}
else
{
var error = await response.Content.ReadAsStringAsync();
throw new Exception($"Failed to get token: {response.StatusCode} - {error}");
}
}
public static async Task Main()
{
try
{
var token = await GetAccessToken();
Console.WriteLine("Ready to make API calls!");
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
)
const baseURL = "https://www.bolodoc.io"
type TokenRequest struct {
APIKey string `json:"api_key"`
}
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
Role string `json:"role"`
}
func getAccessToken() (string, error) {
apiKey := os.Getenv("BOLO_API_KEY")
requestBody := TokenRequest{APIKey: apiKey}
jsonData, err := json.Marshal(requestBody)
if err != nil {
return "", err
}
resp, err := http.Post(
baseURL+"/v1/auth/token",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode == 200 {
var tokenResp TokenResponse
err = json.Unmarshal(body, &tokenResp)
if err != nil {
return "", err
}
fmt.Printf("Access token: %s\n", tokenResp.AccessToken)
fmt.Printf("Expires in: %d seconds\n", tokenResp.ExpiresIn)
fmt.Printf("Your role: %s\n", tokenResp.Role)
return tokenResp.AccessToken, nil
}
return "", fmt.Errorf("failed to get token: %d - %s", resp.StatusCode, string(body))
}
func main() {
token, err := getAccessToken()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Ready to make API calls!")
}
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"role": "public",
"redirect_url": "/v1/auth/profile"
}
Use the /v1/search/simple endpoint for wildcard-based searches. Available to all authenticated users.
Request:
POST https://www.bolodoc.io/v1/search/simple
Authorization: Bearer {your-access-token}
Content-Type: application/json
{
"filters": [
{"field": "title", "value": "Murder*"}
],
"logic": "AND",
"limit": 25
}
Request Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
filters |
array | Yes | - | One or more filter objects. Each object requires a field name and a value string with optional * wildcard characters. |
logic |
string | No | AND |
How to combine multiple filters when more than one is provided. AND returns only records that satisfy every filter. OR returns records that satisfy at least one filter. |
limit |
integer | No | 25 |
Maximum number of records to return. Accepted values: 25, 50, 100, 250, 500, 5000. Your subscription tier enforces a ceiling: Basic and Premium Monthly are capped at 25 regardless of the value you submit. Premium Annual subscribers may request up to 5000. |
rules |
string | No | strict |
Controls how the API handles string values that contain no * wildcard. See the explanation below. |
The rules Parameter: strict vs flex
The rules parameter controls whether the API automatically expands string field values that contain no wildcard character.
| Mode | Behavior when no wildcard is present | Input value | Effective match type |
|---|---|---|---|
strict (default) |
No expansion occurs. A bare value with no * is treated as a case-insensitive exact match. |
"Male" |
sex equals "Male" exactly |
flex |
String fields without any wildcard are automatically wrapped as *value*, producing a contains match. Integer and date fields are never affected by flex mode. |
"murder" |
title contains "murder" (same as supplying *murder*) |
Use strict when precision matters and you are already placing wildcards exactly where needed. Use flex when you want broad contains-style matching without typing * on every filter value.
Request Body Examples:
Example 1: Single filter, starts-with wildcard
Trailing * matches any title beginning with "Murder". logic and limit are omitted, so both default values apply (AND and 25 respectively).
{
"filters": [
{
"field": "title",
"value": "Murder*"
}
]
}
Example 2: Multiple filters with AND logic
Both conditions must be true. The sex filter has no wildcard, so in strict mode (the default) it is an exact match. The title filter uses *...* for a contains match.
{
"filters": [
{
"field": "sex",
"value": "Male"
},
{
"field": "title",
"value": "*murder*"
}
],
"logic": "AND",
"limit": 25
}
Example 3: Multiple filters with OR logic
A record is returned if it matches either filter. Useful for searching across multiple subject categories or aliases in a single call.
{
"filters": [
{
"field": "title",
"value": "*robbery*"
},
{
"field": "title",
"value": "*kidnapping*"
}
],
"logic": "OR",
"limit": 50
}
Example 4: Array field search (languages, field_offices, subjects)
Array fields store multiple values per record. A bare value with no wildcard in strict mode checks whether any element in the array exactly matches the value. Field office names must be lowercase and joined (e.g., losangeles).
{
"filters": [
{
"field": "languages",
"value": "Spanish"
},
{
"field": "field_offices",
"value": "miami"
}
],
"logic": "AND",
"limit": 25
}
Example 5: flex mode — contains matching without explicit wildcards
Setting rules to flex automatically wraps string field values that contain no * as *value*, producing a contains match. Here both caution and description values are expanded automatically. Integer and date fields are never affected by flex mode.
{
"filters": [
{
"field": "caution",
"value": "armed"
},
{
"field": "description",
"value": "dangerous"
}
],
"logic": "OR",
"rules": "flex",
"limit": 25
}
The /v1/search/advanced endpoint is available to Premium subscribers. It supports explicit comparison operators, numeric and date range queries, and grouped conditions with independent AND/OR logic both within and between groups.
Request Structure:
POST https://www.bolodoc.io/v1/search/advanced
Authorization: Bearer {your-access-token}
Content-Type: application/json
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "title", "operator": "contains", "value": "murder"},
{"field": "reward_max", "operator": "gte", "value": 10000}
]
}
],
"group_logic": "AND",
"limit": 25
}
Request Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
groups |
array | Yes | - | One or more filter group objects. Each group contains a condition and a rules array. At least one group with at least one rule is required. |
groups[].condition |
string | Yes | - | How to combine the rules within that specific group. AND means all rules in the group must match. OR means at least one rule in the group must match. |
groups[].rules |
array | Yes | - | One or more rule objects. Each rule requires a field, an operator, and a value. For the between operator, value must be an array of exactly two values in [min, max] order. |
group_logic |
string | No | AND |
How to combine the results of multiple groups. AND requires all groups to match. OR requires at least one group to match. Only meaningful when two or more groups are provided. |
limit |
integer | No | 25 |
Maximum records to return. Accepted values: 25, 50, 100, 250, 500, 5000. Your subscription tier caps the maximum: Premium Monthly is capped at 25; Premium Annual may request up to 5000. |
How Groups and Conditions Interact
Advanced search applies logic at two levels. The condition field inside each group controls how its own rules are evaluated. The top-level group_logic field controls how the evaluated result of each group is combined against the others.
// Logical structure:
// (Group 1 result) [group_logic] (Group 2 result)
//
// Group 1 result = rule_A [condition] rule_B [condition] rule_C
// Group 2 result = rule_D [condition] rule_E
//
// Example with group_logic=OR, condition=AND in each group:
// (title contains "murder" AND sex = "Male") OR (reward_max >= 100000)
Request Body Examples:
Example 1: String fields — contains, equals, ends_with (single AND group)
All three rules must be true. Demonstrates three string operators in one group. contains and ends_with are case-insensitive substring matches; equals is a case-insensitive exact match.
{
"group_logic": "AND",
"groups": [
{
"condition": "AND",
"rules": [
{
"field": "title",
"operator": "contains",
"value": "murder"
},
{
"field": "sex",
"operator": "equals",
"value": "Male"
},
{
"field": "title",
"operator": "ends_with",
"value": "Jr"
}
]
}
],
"limit": 25
}
Example 2: Numeric fields — gte, lte, between (integer operators)
Demonstrates all three forms of numeric range filtering. The first rule uses two separate rules to bracket an age range; the second rule uses between as a shorthand for the same pattern on reward. between accepts an array of exactly two integers in [min, max] order and is inclusive on both ends.
{
"group_logic": "AND",
"groups": [
{
"condition": "AND",
"rules": [
{
"field": "age_min",
"operator": "gte",
"value": 30
},
{
"field": "age_max",
"operator": "lte",
"value": 50
},
{
"field": "reward_max",
"operator": "between",
"value": [10000, 100000]
}
]
}
],
"limit": 25
}
Example 3: Timestamp fields — gt and between (date operators)
Date values must be strings in YYYY-MM-DD format. gt returns records modified strictly after the given date. between on a date field is inclusive on both ends and covers the full calendar day for each boundary.
{
"group_logic": "AND",
"groups": [
{
"condition": "AND",
"rules": [
{
"field": "publication",
"operator": "gt",
"value": "2020-01-01"
},
{
"field": "modified",
"operator": "between",
"value": ["2024-01-01", "2024-12-31"]
}
]
}
],
"limit": 25
}
Example 4: Array fields — contains and equals (text array operators)
Array fields (subjects, languages, field_offices, possible_states, etc.) store multiple values per record. The contains operator checks whether any element in the array matches the substring; equals checks for an exact element match. Field office values must be lowercase and joined (e.g., losangeles). State values use ISO 3166-2 format (US-CA) but the API also accepts the full state name and normalizes it.
{
"group_logic": "AND",
"groups": [
{
"condition": "AND",
"rules": [
{
"field": "subjects",
"operator": "contains",
"value": "Kidnapping"
},
{
"field": "languages",
"operator": "equals",
"value": "Spanish"
},
{
"field": "field_offices",
"operator": "equals",
"value": "miami"
},
{
"field": "possible_states",
"operator": "equals",
"value": "US-FL"
}
]
}
],
"limit": 25
}
Example 5: OR condition within a single group
Setting condition to OR inside a group means a record is returned if it satisfies any of the rules. Useful when you want to match multiple possible values for the same field without creating separate groups.
{
"group_logic": "AND",
"groups": [
{
"condition": "OR",
"rules": [
{
"field": "subjects",
"operator": "contains",
"value": "Murder"
},
{
"field": "subjects",
"operator": "contains",
"value": "Assault"
},
{
"field": "subjects",
"operator": "contains",
"value": "Robbery"
}
]
}
],
"limit": 50
}
Example 6: Multiple groups with OR group_logic
Each group is evaluated independently using its own condition. The group_logic then combines those results. Here, a record is returned if it satisfies Group 1 entirely (a high-value male fugitive) OR Group 2 entirely (any terrorism subject with a recent publication date). This is the mechanism for expressing compound boolean logic that cannot be represented in a single flat list of rules.
{
"group_logic": "OR",
"groups": [
{
"condition": "AND",
"rules": [
{
"field": "sex",
"operator": "equals",
"value": "Male"
},
{
"field": "reward_max",
"operator": "gte",
"value": 100000
}
]
},
{
"condition": "AND",
"rules": [
{
"field": "subjects",
"operator": "contains",
"value": "Terrorism"
},
{
"field": "publication",
"operator": "gte",
"value": "2015-01-01"
}
]
}
],
"limit": 50
}
Both Simple and Advanced search endpoints accept the following fields. Use the exact field names shown below.
String Fields
| Field Name | Description |
|---|---|
title | Person's name or case title |
description | Brief description of the person |
details | Detailed case information |
sex | Gender (Male, Female) |
race | Race/ethnicity (cleaned) |
race_raw | Race/ethnicity (original FBI data) |
nationality | Country of citizenship |
place_of_birth | Birth location |
hair | Hair color (cleaned) |
hair_raw | Hair color (original FBI data) |
eyes | Eye color (cleaned) |
eyes_raw | Eye color (original FBI data) |
build | Body build description |
complexion | Skin complexion |
weight | Weight description (text) |
scars_and_marks | Identifying marks, tattoos, scars |
caution | Warning information |
warning_message | Safety warnings |
remarks | Additional notes |
reward_text | Reward description text |
status | Case status |
ncic | NCIC number |
path | FBI URL path |
pathid | FBI path identifier |
url | Full FBI URL |
poster_url | Wanted poster image URL |
person_classification | Person type classification |
poster_classification | Poster type classification |
Integer Fields
| Field Name | Description |
|---|---|
age_min | Minimum estimated age |
age_max | Maximum estimated age |
height_min | Minimum height (inches) |
height_max | Maximum height (inches) |
weight_min | Minimum weight (pounds) |
weight_max | Maximum weight (pounds) |
reward_min | Minimum reward amount (USD) |
reward_max | Maximum reward amount (USD) |
Timestamp Fields
| Field Name | Description |
|---|---|
modified | Last modified date in FBI database |
publication | Original publication date |
Array Fields
| Field Name | Description |
|---|---|
aliases | Known aliases and alternate names |
dates_of_birth_used | Known dates of birth used |
field_offices |
FBI field offices handling the case show values |
languages |
Languages spoken show values |
locations | Known locations |
occupations | Known occupations |
possible_countries |
Countries where person may be located (ISO 3166 codes) show values |
possible_states |
US states where person may be located show values |
subjects | Crime categories/subjects |
Simple Search (Wildcard Patterns)
Simple search uses wildcard patterns with the * character:
| Pattern | Behavior | Example |
|---|---|---|
*text* | Contains | {"field": "title", "value": "*murder*"} |
text* | Starts with | {"field": "title", "value": "John*"} |
*text | Ends with | {"field": "title", "value": "*Smith"} |
text | Exact match | {"field": "sex", "value": "Male"} |
Advanced Search (Explicit Operators)
Advanced search uses explicit operator names for precise control:
Text Operators
| Operator | Description | Example |
|---|---|---|
equals | Exact match (case-insensitive) | {"field": "sex", "operator": "equals", "value": "Male"} |
contains | Contains substring | {"field": "title", "operator": "contains", "value": "murder"} |
starts_with | Starts with text | {"field": "title", "operator": "starts_with", "value": "John"} |
ends_with | Ends with text | {"field": "title", "operator": "ends_with", "value": "Smith"} |
Numeric/Date Operators
| Operator | Description | Example |
|---|---|---|
equals | Equal to | {"field": "age_min", "operator": "equals", "value": 30} |
gt | Greater than | {"field": "reward_max", "operator": "gt", "value": 10000} |
lt | Less than | {"field": "age_max", "operator": "lt", "value": 50} |
gte | Greater than or equal | {"field": "reward_min", "operator": "gte", "value": 5000} |
lte | Less than or equal | {"field": "height_max", "operator": "lte", "value": 72} |
between | Between two values | {"field": "reward_max", "operator": "between", "value": [5000, 50000]} |
Search endpoints support multiple response formats via the format query parameter.
| Format | Access | Description |
|---|---|---|
json | Basic, Premium | Default JSON format with full metadata |
csv | Premium | Comma-separated values for spreadsheet import |
parquet | Premium | Columnar format for cloud-based data lake architectures |
txt | Premium | Human-readable plain text BOLO format |
xml | Premium | Structured XML for RMS/CAD system integration |
Usage:
Append the format query parameter to the endpoint URL. The request body is unchanged — only the URL changes.
POST https://www.bolodoc.io/v1/search/simple?format=csv
Authorization: Bearer {your-access-token}
Content-Type: application/json
{
"filters": [
{
"field": "title",
"value": "*murder*"
}
],
"limit": 250
}
The same pattern applies to the advanced endpoint:
POST https://www.bolodoc.io/v1/search/advanced?format=xml
Authorization: Bearer {your-access-token}
Content-Type: application/json
{
"groups": [
{
"condition": "AND",
"rules": [
{
"field": "subjects",
"operator": "contains",
"value": "Terrorism"
}
]
}
],
"limit": 100
}
The following examples cover a wide range of real-world scenarios. Each shows the full request body. All string comparisons are case-insensitive unless noted. Simple search examples use POST /v1/search/simple. Advanced search examples use POST /v1/search/advanced and require a Premium subscription.
Simple Search Examples
Use Case: Find all subjects whose name contains the word "murder"
Using *murder* pattern to get a contains match on the title field.
{
"filters": [
{"field": "title", "value": "*murder*"}
],
"limit": 25
}
Use Case: Find subjects whose name starts with "John"
Trailing * produces a starts-with match.
{
"filters": [
{"field": "title", "value": "John*"}
],
"limit": 25
}
Use Case: Find subjects whose name ends with "Garcia"
Leading * produces an ends-with match.
{
"filters": [
{"field": "title", "value": "*Garcia"}
],
"limit": 25
}
Use Case: Find all female subjects
No wildcard on an exact field value. rules defaults to strict, so "Female" is an exact match.
{
"filters": [
{"field": "sex", "value": "Female"}
],
"limit": 25
}
Use Case: Find female subjects whose title contains "bank"
Multiple filters combined with AND (the default). Both conditions must be true for a record to appear.
{
"filters": [
{"field": "sex", "value": "Female"},
{"field": "title", "value": "*bank*"}
],
"logic": "AND",
"limit": 25
}
Use Case: Find subjects wanted for robbery OR kidnapping in the title
Using logic: "OR" so that a record matching either filter is returned.
{
"filters": [
{"field": "title", "value": "*robbery*"},
{"field": "title", "value": "*kidnapping*"}
],
"logic": "OR",
"limit": 50
}
Use Case: Find subjects with any caution warning mentioning weapons
Searching the caution field which contains FBI-issued safety warnings.
{
"filters": [
{"field": "caution", "value": "*weapon*"}
],
"limit": 25
}
Use Case: Find subjects who speak Spanish
The languages field is an array. A contains match checks whether any element in the array matches.
{
"filters": [
{"field": "languages", "value": "Spanish"}
],
"limit": 25
}
Use Case: Find subjects handled by the Miami field office
Field office values are lowercase and single-word. "miami" is the correct format.
{
"filters": [
{"field": "field_offices", "value": "miami"}
],
"limit": 25
}
Use Case: Find subjects possibly located in California
The possible_states field uses ISO 3166-2 format. You may supply the full state name and the API normalizes it automatically.
{
"filters": [
{"field": "possible_states", "value": "California"}
],
"limit": 25
}
Use Case: Find subjects possibly located in California OR Texas
OR logic across two state filters. Both US-CA and US-TX formats are accepted, or you can supply the full name.
{
"filters": [
{"field": "possible_states", "value": "California"},
{"field": "possible_states", "value": "Texas"}
],
"logic": "OR",
"limit": 50
}
Use Case: Find subjects possibly located in Mexico
The possible_countries field uses ISO 3166 alpha-3 codes, but the API accepts country names and normalizes them.
{
"filters": [
{"field": "possible_countries", "value": "Mexico"}
],
"limit": 25
}
Use Case: Broad keyword search without typing wildcards (flex mode)
Setting rules: "flex" automatically wraps string values without wildcards as a contains match. The input "armed" becomes *armed* for string fields. Integer and date fields are unaffected.
{
"filters": [
{"field": "caution", "value": "armed"},
{"field": "description", "value": "dangerous"}
],
"logic": "OR",
"rules": "flex",
"limit": 25
}
Use Case: Exact match with strict mode explicitly declared
Declaring rules: "strict" (the default) ensures "White Collar Crime" is an exact match on the subjects field, not a contains search.
{
"filters": [
{"field": "subjects", "value": "White Collar Crime"}
],
"rules": "strict",
"limit": 25
}
Use Case: Get up to 100 results for a broad name search (Premium Annual required)
The limit is 100. Basic subscribers are capped at 25 regardless of this value. Only Premium Annual subscribers may retrieve up to 5000.
{
"filters": [
{"field": "title", "value": "Jose*"}
],
"limit": 100
}
Use Case: Find subjects with tattoo or scar descriptions mentioning a specific body part
Searching the scars_and_marks field for identifying feature references.
{
"filters": [
{"field": "scars_and_marks", "value": "*neck*"}
],
"limit": 25
}
Use Case: Find subjects known to use the alias "El Chapo"
The aliases field is an array of known names. A contains match scans all alias entries.
{
"filters": [
{"field": "aliases", "value": "*Chapo*"}
],
"limit": 25
}
Advanced Search Examples PREMIUM
Use Case: Find subjects with rewards over $50,000
Single group, single rule using the gte operator on a numeric field.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "reward_max", "operator": "gte", "value": 50000}
]
}
],
"limit": 25
}
Use Case: Find male subjects wanted for murder with a reward of at least $10,000
Three rules in one group, all joined by AND. Every condition must be satisfied.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "sex", "operator": "equals", "value": "Male"},
{"field": "title", "operator": "contains", "value": "murder"},
{"field": "reward_max", "operator": "gte", "value": 10000}
]
}
],
"group_logic": "AND",
"limit": 25
}
Use Case: Find subjects in an estimated age range of 25 to 40
Using two rules on the same group to bracket a numeric range. Alternatively, the between operator can be used on a single rule.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "age_min", "operator": "gte", "value": 25},
{"field": "age_max", "operator": "lte", "value": 40}
]
}
],
"limit": 25
}
Use Case: Find subjects whose reward falls between $5,000 and $100,000
The between operator accepts an array of exactly two values: [min, max]. Both endpoints are inclusive.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "reward_max", "operator": "between", "value": [5000, 100000]}
]
}
],
"limit": 25
}
Use Case: Find subjects whose records were modified in a specific date range
Date values must be in YYYY-MM-DD format. The between operator is inclusive on both ends for date fields.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "modified", "operator": "between", "value": ["2024-01-01", "2024-12-31"]}
]
}
],
"limit": 25
}
Use Case: Find subjects whose records were added or modified after a specific date
Using gt (greater than) on the publication date field.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "publication", "operator": "gt", "value": "2023-06-01"}
]
}
],
"limit": 50
}
Use Case: Find subjects with specific physical dimensions
Height is stored in inches. This query finds subjects estimated between 5'6" (66 in) and 6'0" (72 in) tall.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "height_min", "operator": "gte", "value": 66},
{"field": "height_max", "operator": "lte", "value": 72}
]
}
],
"limit": 25
}
Use Case: Find subjects wanted for kidnapping handled by the Miami or Los Angeles offices
OR condition within a single group. The subject must be wanted for kidnapping AND (handled by miami OR losangeles). Note: to express that logic, use two separate groups with OR group_logic, because a single group with mixed AND/OR is not supported.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "subjects", "operator": "contains", "value": "Kidnapping"},
{"field": "field_offices", "operator": "equals", "value": "miami"}
]
},
{
"condition": "AND",
"rules": [
{"field": "subjects", "operator": "contains", "value": "Kidnapping"},
{"field": "field_offices", "operator": "equals", "value": "losangeles"}
]
}
],
"group_logic": "OR",
"limit": 25
}
Use Case: Find subjects who match either a high-reward profile OR a terrorism-related category
Two groups joined by OR group_logic. A record matching either group will be returned.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "reward_max", "operator": "gte", "value": 100000}
]
},
{
"condition": "AND",
"rules": [
{"field": "subjects", "operator": "contains", "value": "Terrorism"}
]
}
],
"group_logic": "OR",
"limit": 50
}
Use Case: Find male subjects over 40 who speak Spanish and may be in Mexico or Venezuela
A multi-condition group combining demographic, language, and location filters. All conditions are joined by AND.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "sex", "operator": "equals", "value": "Male"},
{"field": "age_min", "operator": "gte", "value": 40},
{"field": "languages", "operator": "contains", "value": "Spanish"},
{"field": "possible_countries", "operator": "equals", "value": "MEX"}
]
},
{
"condition": "AND",
"rules": [
{"field": "sex", "operator": "equals", "value": "Male"},
{"field": "age_min", "operator": "gte", "value": 40},
{"field": "languages", "operator": "contains", "value": "Spanish"},
{"field": "possible_countries", "operator": "equals", "value": "VEN"}
]
}
],
"group_logic": "OR",
"limit": 25
}
Use Case: Find subjects with white collar crime ties and no reward (reward_max equals 0)
Combining a category filter with a numeric equals check. Useful to identify cases where no reward has been posted.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "subjects", "operator": "contains", "value": "White Collar Crime"},
{"field": "reward_max", "operator": "equals", "value": 0}
]
}
],
"limit": 25
}
Use Case: Find subjects whose title starts with "John" and are shorter than 5'8" (68 inches)
Mixing a string operator (starts_with) with a numeric operator (lt) in a single group.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "title", "operator": "starts_with", "value": "John"},
{"field": "height_max", "operator": "lt", "value": 68}
]
}
],
"limit": 25
}
Use Case: Find subjects wanted for cyber crime with records modified in 2025
Combining a subjects array contains check with a date range, using a single AND group.
{
"groups": [
{
"condition": "AND",
"rules": [
{"field": "subjects", "operator": "contains", "value": "Cyber"},
{"field": "modified", "operator": "gte", "value": "2025-01-01"},
{"field": "modified", "operator": "lte", "value": "2025-12-31"}
]
}
],
"limit": 25
}
Use Case: Export up to 500 records for subjects wanted for violent crimes (Premium Annual, CSV format)
Requesting a higher limit and appending ?format=csv to the endpoint URL. Only Premium Annual subscribers may retrieve up to 500 or 5000 records. The format query parameter is appended to the URL, not the request body.
POST https://www.bolodoc.io/v1/search/advanced?format=csv
{
"groups": [
{
"condition": "OR",
"rules": [
{"field": "subjects", "operator": "contains", "value": "Murder"},
{"field": "subjects", "operator": "contains", "value": "Assault"},
{"field": "subjects", "operator": "contains", "value": "Robbery"}
]
}
],
"limit": 500
}
/v1/search/simple and /v1/search/advanced to view the JSON response structure.
Token Limits:
- Tokens expire after 60 minutes
- Request a new token before expiration
Result Limits by Subscription:
| Plan | Max Results | Data Quality | Features |
|---|---|---|---|
| Basic | 25 per search | Raw data | Simple search, JSON only |
| Premium (Monthly) | 25 per search | Cleaned data | Simple + Advanced search, All formats |
| Premium (Annual) | 5,000 per search | Cleaned data | All features + Category endpoints + Document archive |
Best Practices:
- Cache tokens and reuse them until expiration
- Handle rate limit errors (429 status code) gracefully
- Use specific filters to narrow results
- Implement exponential backoff for retries
- Store your API key securely (environment variables, key vault)
- Never hardcode API keys in your source code
- View full API documentation - Interactive Swagger UI with endpoints public
- Manage your profile - Update settings or reset your API key