Search K
Filtering Guide
This guide explains how to filter data when making GET requests to the ezyVet API. Filtering allows you to retrieve specific records based on field values and comparison operators.
Overview
The ezyVet API supports field comparators using URL-encoded JSON format. Each filterable field accepts multiple comparators that allow you to perform various types of comparisons.
Key Points:
- Filters are passed as query parameters
- Filter values use URL-encoded JSON format:
field_name={"comparator":value} - Multiple filters can be combined in a single request
- All filters are case-sensitive
- Field names must match exactly as documented in the API
Supported Comparators
The following comparators are supported by the ezyVet API:
| Comparator | Code | Arithmetic | Supported Types | Description |
|---|---|---|---|---|
| Equal | eq | = | All | Exact match |
| Not equal | neq | != | All | Not equal to |
| Greater than | gt | > | Number, Date, Datetime | Greater than |
| Less than | lt | < | Number, Date, Datetime | Less than |
| Greater than or equal | gte | >= | Number, Date, Datetime | Greater than or equal |
| Less than or equal | lte | <= | Number, Date, Datetime | Less than or equal |
| Like | like | - | String only | Pattern matching (string fields only) |
| In | in | - | String, Number, Date | Match any value in a list |
Filter Format
Filters use URL-encoded JSON format. The general structure is:
field_name={"comparator":value}For the in comparator, use an array:
field_name={"in":[value1,value2,value3]}For range queries, combine comparators:
field_name={"gte":value1,"lte":value2}Examples
Large initial syncing
Often there is a need to pull a large number of records from a database as part of an initial load/sync. Due to the way the ezyVet API is built, navigating through thousands of pages can cause extensive load, resulting in a long response time. By reducing the context of the search, this can be mitigated.
It's recommended to partition the sync by following the below steps:
- Using the
active,idandlimitquery parameters, pull a certain number of records into context and enlargen the page size by using thegtandltfield comparators.
E.g. Fetch all active contact records where the ID is greater than 0 and less than 1001 and make the page size 200.
HTTP
GET /v2/contact?id={"gt":0, "lt":1001}&active=true&limit=200 HTTP/1.1
Host: api.trial.ezyvet.com
Authorization: Bearer token- With only 1000 objects to parse and 200 objects appearring per page, only 5 pages need to be loaded. By using the
pagequery parameter, we can navigate the data easily and efficiently.
HTTP
GET /v2/contact?id={"gt":0, "lt":1001}&active=true&limit=200&page=1 HTTP/1.1
Host: api.trial.ezyvet.com
Authorization: Bearer token- Once the data from all 5 pages have been loaded, the process can be incremented based on the ID.
HTTP
GET /v2/contact?id={"gt":1000, "lt":2001}&active=true&limit=200&page=1 HTTP/1.1
Host: api.trial.ezyvet.com
Authorization: Bearer tokenBasic Examples
1. Equal (eq) - Simple Value
Get all animals with a specific ID:
cURL:
bash
curl --location 'https://api.trial.ezyvet.com/v1/animal?id={"eq":123}' \
--header 'Authorization: Bearer YOUR_TOKEN'Python:
python
import json
import urllib.parse
import urllib.request
# Build filter
filter_value = json.dumps({"eq": 123}, separators=(',', ':'))
params = {'id': filter_value}
query_string = urllib.parse.urlencode(params)
url = f"https://api.trial.ezyvet.com/v1/animal?{query_string}"
# Make request
req = urllib.request.Request(url, headers={'Authorization': f'Bearer {token}'})
response = urllib.request.urlopen(req)TypeScript:
typescript
const filterValue = JSON.stringify({ eq: 123 });
const params = new URLSearchParams({ id: filterValue });
const url = `https://api.trial.ezyvet.com/v1/animal?${params}`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${token}`
}
});2. Greater Than or Equal (gte) - Timestamp Filter
Get animals modified in the last 24 hours:
cURL:
bash
# Calculate timestamp (Unix epoch)
TIMESTAMP=$(date -u -d '24 hours ago' +%s)
curl --location "https://api.trial.ezyvet.com/v1/animal?modified_at={\"gte\":${TIMESTAMP}}" \
--header 'Authorization: Bearer YOUR_TOKEN'Python:
python
from datetime import datetime, timedelta
import json
import urllib.parse
# Calculate timestamp for 24 hours ago
twenty_four_hours_ago = datetime.now() - timedelta(hours=24)
timestamp = int(twenty_four_hours_ago.timestamp())
# Build filter
filter_json = json.dumps({"gte": timestamp}, separators=(',', ':'))
params = {'modified_at': filter_json}
query_string = urllib.parse.urlencode(params)
url = f"https://api.trial.ezyvet.com/v1/animal?{query_string}"TypeScript:
typescript
// Calculate timestamp for 24 hours ago
const twentyFourHoursAgo = new Date();
twentyFourHoursAgo.setHours(twentyFourHoursAgo.getHours() - 24);
const timestamp = Math.floor(twentyFourHoursAgo.getTime() / 1000);
// Build filter
const filterValue = JSON.stringify({ gte: timestamp });
const params = new URLSearchParams({ modified_at: filterValue });
const url = `https://api.trial.ezyvet.com/v1/animal?${params}`;3. Range Query (gte and lte) - Date Range
Get appointments starting from today through the next 5 days:
cURL:
bash
# Calculate timestamps
TODAY=$(date -u -d 'today 00:00:00' +%s)
FIVE_DAYS=$(date -u -d 'today +5 days 23:59:59' +%s)
curl --location "https://api.trial.ezyvet.com/v1/appointment?start_time={\"gte\":${TODAY},\"lte\":${FIVE_DAYS}}" \
--header 'Authorization: Bearer YOUR_TOKEN'Python:
python
from datetime import datetime, timedelta
import json
import urllib.parse
# Calculate timestamps
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
five_days_from_now = today + timedelta(days=5)
five_days_from_now = five_days_from_now.replace(hour=23, minute=59, second=59)
today_timestamp = int(today.timestamp())
five_days_timestamp = int(five_days_from_now.timestamp())
# Build filter with range
start_time_filter = json.dumps(
{"gte": today_timestamp, "lte": five_days_timestamp},
separators=(',', ':')
)
params = {'start_time': start_time_filter}
query_string = urllib.parse.urlencode(params)
url = f"https://api.trial.ezyvet.com/v1/appointment?{query_string}"TypeScript:
typescript
// Calculate timestamps
const today = new Date();
today.setHours(0, 0, 0, 0);
const fiveDaysFromNow = new Date(today);
fiveDaysFromNow.setDate(fiveDaysFromNow.getDate() + 5);
fiveDaysFromNow.setHours(23, 59, 59, 999);
const todayTimestamp = Math.floor(today.getTime() / 1000);
const fiveDaysTimestamp = Math.floor(fiveDaysFromNow.getTime() / 1000);
// Build filter with range
const filterValue = JSON.stringify({ gte: todayTimestamp, lte: fiveDaysTimestamp });
const params = new URLSearchParams({ start_time: filterValue });
const url = `https://api.trial.ezyvet.com/v1/appointment?${params}`;4. In (in) - Multiple Values
Get appointments with specific appointment type IDs:
cURL:
bash
curl --location 'https://api.trial.ezyvet.com/v1/appointment?appointment_type_id={"in":[1,9,15]}' \
--header 'Authorization: Bearer YOUR_TOKEN'Python:
python
import json
import urllib.parse
# List of appointment type IDs
type_ids = [1, 9, 15]
# Build filter using "in" comparator
appointment_type_id_filter = json.dumps({"in": type_ids}, separators=(',', ':'))
params = {'appointment_type_id': appointment_type_id_filter}
query_string = urllib.parse.urlencode(params)
url = f"https://api.trial.ezyvet.com/v1/appointment?{query_string}"TypeScript:
typescript
// List of appointment type IDs
const typeIds = [1, 9, 15];
// Build filter using "in" comparator
const filterValue = JSON.stringify({ in: typeIds });
const params = new URLSearchParams({ appointment_type_id: filterValue });
const url = `https://api.trial.ezyvet.com/v1/appointment?${params}`;Combining Multiple Filters
You can combine multiple filters in a single request:
cURL:
bash
curl --location 'https://api.trial.ezyvet.com/v1/appointment?start_time={"gte":1729455014,"lte":1729728000}&appointment_type_id={"in":[1,9,15]}&active=true' \
--header 'Authorization: Bearer YOUR_TOKEN'Python:
python
import json
import urllib.parse
# Multiple filters
start_time_filter = json.dumps({"gte": 1729455014, "lte": 1729728000}, separators=(',', ':'))
appointment_type_id_filter = json.dumps({"in": [1, 9, 15]}, separators=(',', ':'))
params = {
'start_time': start_time_filter,
'appointment_type_id': appointment_type_id_filter,
'active': 'true' # Simple boolean values don't need JSON encoding
}
query_string = urllib.parse.urlencode(params)
url = f"https://api.trial.ezyvet.com/v1/appointment?{query_string}"TypeScript:
typescript
// Multiple filters
const startTimeFilter = JSON.stringify({ gte: 1729455014, lte: 1729728000 });
const appointmentTypeIdFilter = JSON.stringify({ in: [1, 9, 15] });
const params = new URLSearchParams({
start_time: startTimeFilter,
appointment_type_id: appointmentTypeIdFilter,
active: 'true' // Simple boolean values don't need JSON encoding
});
const url = `https://api.trial.ezyvet.com/v1/appointment?${params}`;Advanced Examples
5. Like (like) - Pattern Matching
Search for animals with names containing "Max":
cURL:
bash
curl --location 'https://api.trial.ezyvet.com/v1/animal?name={"like":"%Max%"}' \
--header 'Authorization: Bearer YOUR_TOKEN'Python:
python
import json
import urllib.parse
# Pattern matching filter
name_filter = json.dumps({"like": "%Max%"}, separators=(',', ':'))
params = {'name': name_filter}
query_string = urllib.parse.urlencode(params)
url = f"https://api.trial.ezyvet.com/v1/animal?{query_string}"TypeScript:
typescript
const filterValue = JSON.stringify({ like: "%Max%" });
const params = new URLSearchParams({ name: filterValue });
const url = `https://api.trial.ezyvet.com/v1/animal?${params}`;TypeScript:
typescript
const filterValue = JSON.stringify({ neq: false });
const params = new URLSearchParams({ active: filterValue });
const url = `https://api.trial.ezyvet.com/v1/animal?${params}`;Complete Working Examples
Example 1: Get Animals Modified in Last 24 Hours
This example demonstrates filtering by timestamp using the gte comparator.
Python:
python
#!/usr/bin/env python3
import os
import json
import urllib.request
import urllib.parse
from datetime import datetime, timedelta
API_BASE_URL = os.getenv('API_BASE_URL', 'https://api.trial.ezyvet.com')
TOKEN = os.getenv('TOKEN', '')
def get_animals_modified_last_24_hours(token):
"""Get animals modified in the last 24 hours."""
# Calculate timestamp for 24 hours ago
twenty_four_hours_ago = datetime.now() - timedelta(hours=24)
timestamp = int(twenty_four_hours_ago.timestamp())
# Build filter with gte comparator
filter_json = json.dumps({"gte": timestamp}, separators=(',', ':'))
params = {'modified_at': filter_json}
query_string = urllib.parse.urlencode(params)
api_url = f"{API_BASE_URL}/v1/animal?{query_string}"
headers = {
'Authorization': f'Bearer {token}',
'Accept': 'application/json',
}
req = urllib.request.Request(api_url, headers=headers)
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode('utf-8'))
return data.get('items', [])
# Usage
animals = get_animals_modified_last_24_hours(TOKEN)
print(f"Found {len(animals)} animals")TypeScript:
typescript
async function getAnimalsModifiedLast24Hours(token: string): Promise<any[]> {
// Calculate timestamp for 24 hours ago
const twentyFourHoursAgo = new Date();
twentyFourHoursAgo.setHours(twentyFourHoursAgo.getHours() - 24);
const timestamp = Math.floor(twentyFourHoursAgo.getTime() / 1000);
// Build filter with gte comparator
const filterValue = JSON.stringify({ gte: timestamp });
const params = new URLSearchParams({ modified_at: filterValue });
const url = `https://api.trial.ezyvet.com/v1/animal?${params}`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
});
const data = await response.json();
return data.items || [];
}
// Usage
const animals = await getAnimalsModifiedLast24Hours(process.env.TOKEN!);
console.log(`Found ${animals.length} animals`);Example 2: Get Appointments with Multiple Filters
This example demonstrates combining date range filtering with an "in" filter for appointment types.
Python:
python
#!/usr/bin/env python3
import os
import json
import urllib.request
import urllib.parse
from datetime import datetime, timedelta
API_BASE_URL = os.getenv('API_BASE_URL', 'https://api.trial.ezyvet.com')
TOKEN = os.getenv('TOKEN', '')
def get_appointments_next_5_days(token, type_ids):
"""Get appointments for next 5 days, filtered by appointment type IDs."""
# Calculate timestamps
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
five_days_from_now = today + timedelta(days=5)
five_days_from_now = five_days_from_now.replace(hour=23, minute=59, second=59)
today_timestamp = int(today.timestamp())
five_days_timestamp = int(five_days_from_now.timestamp())
# Build filters
start_time_filter = json.dumps(
{"gte": today_timestamp, "lte": five_days_timestamp},
separators=(',', ':')
)
appointment_type_id_filter = json.dumps({"in": type_ids}, separators=(',', ':'))
params = {
'start_time': start_time_filter,
'appointment_type_id': appointment_type_id_filter
}
query_string = urllib.parse.urlencode(params)
api_url = f"{API_BASE_URL}/v1/appointment?{query_string}"
headers = {
'Authorization': f'Bearer {token}',
'Accept': 'application/json',
}
req = urllib.request.Request(api_url, headers=headers)
with urllib.request.urlopen(req) as response:
data = json.loads(response.read().decode('utf-8'))
return data.get('items', [])
# Usage
type_ids = [1, 9, 15] # Consult, Vaccinations, Recheck
appointments = get_appointments_next_5_days(TOKEN, type_ids)
print(f"Found {len(appointments)} appointments")TypeScript:
typescript
async function getAppointmentsNext5Days(
token: string,
typeIds: number[]
): Promise<any[]> {
// Calculate timestamps
const today = new Date();
today.setHours(0, 0, 0, 0);
const fiveDaysFromNow = new Date(today);
fiveDaysFromNow.setDate(fiveDaysFromNow.getDate() + 5);
fiveDaysFromNow.setHours(23, 59, 59, 999);
const todayTimestamp = Math.floor(today.getTime() / 1000);
const fiveDaysTimestamp = Math.floor(fiveDaysFromNow.getTime() / 1000);
// Build filters
const startTimeFilter = JSON.stringify({
gte: todayTimestamp,
lte: fiveDaysTimestamp
});
const appointmentTypeIdFilter = JSON.stringify({ in: typeIds });
const params = new URLSearchParams({
start_time: startTimeFilter,
appointment_type_id: appointmentTypeIdFilter
});
const url = `https://api.trial.ezyvet.com/v1/appointment?${params}`;
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
});
const data = await response.json();
return data.items || [];
}
// Usage
const typeIds = [1, 9, 15]; // Consult, Vaccinations, Recheck
const appointments = await getAppointmentsNext5Days(process.env.TOKEN!, typeIds);
console.log(`Found ${appointments.length} appointments`);Important Notes
URL Encoding
When building filters, ensure proper URL encoding:
- Use
urllib.parse.urlencode()in Python - Use
URLSearchParamsin TypeScript/JavaScript - Use
--data-urlencodeor proper quoting in cURL
JSON Formatting
- Use
separators=(',', ':')in Python'sjson.dumps()to avoid spaces - Ensure proper JSON formatting (no trailing commas, proper quotes)
- Arrays for
incomparator must be valid JSON arrays:[1,2,3]
Data Types
- Numbers: Use integers or floats directly:
{"gte": 123} - Strings: Use quoted strings:
{"eq": "value"} - Booleans: Use
trueorfalse(not quoted):{"eq": true} - Timestamps: Use Unix epoch timestamps (seconds since 1970-01-01):
{"gte": 1729455014} - Null: Use
null(not quoted):{"eq": null}
Field Names
- Field names are case-sensitive
- Use exact field names as documented in the API
- Different API versions may use different field names (e.g.,
start_timevsstart_at)
Error Handling
If you use an unsupported filter or incorrect format, the API will return a 400 Bad Request error:
json
{
"messages": [
{
"level": "error",
"type": "ValidationException",
"text": "The type id must be a number.",
"fields": ["type_id"]
}
]
}Questions
Some API GET endpoints support Questions.
Questions return results from complex queries about a resource. For example, the Product question stocklevel will return various Product stock level numbers, such as total stock, stock allocated to invoices and so on.
WARNING
It's recommended to use the /inventory/v1/products endpoint to retrieve inventory/batch tracked products and their corrosponding inventory balances.
Endpoints with data that seldom changes
The endpoints below represent categories of data where the data rarely changes. It’s encouraged that data retrieved from these endpoints be cached and refreshed semi-regularly. (Usually once daily is sufficient)
- v1/animalcolour
- v2/appointmenttype
- v1/breed
- v1/contactdetailtype
- v1/country
- v1/paymentmethod
- v1/productgroup
- v2/resource
- v2/separation
- v1/sex
- v1/species
- v1/systemsetting
- v1/tagcategory
- v2/taxrate
- v1/user
- v1/webhookevents
Best Practices
- Cache Static Data: For frequently accessed lookup data (like appointment types), cache the results locally
- Use Appropriate Comparators: Use
gte/ltefor ranges,infor multiple values,eqfor exact matches - Combine Filters Efficiently: Combine multiple filters in a single request rather than making multiple requests
- Handle Pagination: Use
limitandpageparameters for large result sets - Validate Input: Ensure timestamps and IDs are in the correct format before building filters