Skip to main content

Python client for the Wildbook v3 API

Project description

pywildbook

A Python client library for interacting with the Wildbook v3 API. This package provides an interface for authenticating with Wildbook instances and searching for wildlife encounters, individuals, and other data.

Installation

Using uv (recommended)

uv add pywildbook

Using pip

pip install pywildbook

Quick Start

from pywildbook import WildbookClient
from pywildbook.queries import match_all
import os

# Create a client instance
# The base URL can also be set via the WILDBOOK_URL environment variable.
client = WildbookClient(os.environ.get('WILDBOOK_URL', 'http://localhost:8080'))

# Login
# Credentials can be passed directly or sourced from WILDBOOK_USERNAME and WILDBOOK_PASSWORD environment variables.
client.login() 

# Search for encounters
results = client.search_encounters(match_all(), size=10)

# Print results
for encounter in results['hits']:
    print(f"{encounter['id']}: {encounter.get('genus', '')} {encounter.get('specificEpithet', '')}")

# Logout when done
client.logout()

Authentication

The client uses session-based authentication. After logging in, the session cookie is automatically managed for all subsequent requests. For security, it is highly recommended to use environment variables for sensitive credentials.

from pywildbook import WildbookClient
import os

# The base URL can also be set via the WILDBOOK_URL environment variable.
client = WildbookClient(os.environ.get('WILDBOOK_URL', 'http://localhost:8080'))

# Login
# Credentials can be passed directly or sourced from WILDBOOK_USERNAME and WILDBOOK_PASSWORD environment variables.
user_info = client.login() 
print(f"Logged in as: {user_info['username']}")

# Check authentication status
if client.is_authenticated():
    print("✓ Authenticated")

# Get current user info
user = client.get_current_user()
print(f"User ID: {user['id']}")

# Logout
client.logout()

Using Context Manager (Recommended)

The client can be used as a context manager to ensure automatic logout:

import os
from pywildbook import WildbookClient
from pywildbook.queries import match_all

# The base URL can also be set via the WILDBOOK_URL environment variable.
with WildbookClient(os.environ.get('WILDBOOK_URL', 'http://localhost:8080')) as client:
    # Credentials can be passed directly or sourced from WILDBOOK_USERNAME and WILDBOOK_PASSWORD environment variables.
    client.login()
    results = client.search_encounters(match_all())
    # ... do work ...
    # logout() is called automatically when exiting the context

Searching Encounters

Basic Search

from pywildbook.queries import match_all

# Get all encounters
results = client.search_encounters(match_all(), size=50)

# With pagination
results = client.search_encounters(
    match_all(),
    from_=0,      # offset
    size=20,      # page size
    sort='date',  # sort field
    sort_order='desc'
)

My Encounters

from pywildbook.queries import filter_by_species, combine_queries

# Get my 10 most recent encounters
my_encounters = client.search_encounters(
    client.filter_current_user(),
    size=10,
    sort='date',
    sort_order='desc'
)

# Combine with other filters: my encounters of a specific species
query = combine_queries(
    client.filter_current_user(),
    filter_by_species('Megaptera novaeangliae'),
    operator='must'
)
results = client.search_encounters(query)

Filtering by Species

from pywildbook.queries import filter_by_species

# Search by species
query = filter_by_species('novaeangliae')
results = client.search_encounters(query)

# Search by genus and species
query = filter_by_species('Megaptera novaeangliae')
results = client.search_encounters(query)

Filtering by Date Range

from pywildbook.queries import filter_by_date_range

# Encounters since 1 November 2025
query = filter_by_date_range(start_date='2025-11-01')
results = client.search_encounters(query)

# Encounters between two dates
query = filter_by_date_range(start_date='2025-11-01', end_date='2025-12-01')
results = client.search_encounters(query)

Filtering by Location

from pywildbook.queries import filter_by_location

# Filter by country
query = filter_by_location(country='Kenya')
results = client.search_encounters(query)

# Filter by bounding box
query = filter_by_location(
    min_lat=-5.0,
    max_lat=5.0,
    min_lon=35.0,
    max_lon=42.0
)
results = client.search_encounters(query)

Combining Multiple Filters

from pywildbook.queries import (
    filter_by_species,
    filter_by_sex,
    filter_by_year_range,
    combine_queries
)

# Female humpback whales from 2020-2023
species = filter_by_species('Megaptera novaeangliae')
sex = filter_by_sex('female')
years = filter_by_year_range(2020, 2023)

query = combine_queries(species, sex, years, operator='must')
results = client.search_encounters(query)

Finding Unassigned Encounters

from pywildbook.queries import field_missing

# Encounters without an assigned individual
query = field_missing('individualId')
unassigned = client.search_encounters(query)

Text Search

from pywildbook.queries import text_search

# Search for "beach" in locality descriptions
query = text_search('verbatimLocality', 'beach', fuzzy=True)
results = client.search_encounters(query)

Searching Individuals

from pywildbook.queries import field_exists

# Find individuals with encounters
query = field_exists('encounters')
results = client.search_individuals(query, size=20)

for individual in results['hits']:
    print(f"{individual['id']}: {individual.get('displayName', 'Unnamed')}")

Getting Specific Records

# Get a specific encounter by UUID
encounter = client.get_encounter('123e4567-e89b-12d3-a456-426614174000')
print(encounter)

# Get a specific individual by UUID
individual = client.get_individual('987fcdeb-51a2-43f7-9876-543210fedcba')
print(individual)

User Dashboard

# Get dashboard data for the current user
dashboard = client.get_user_home()

print(f"Latest encounters: {dashboard.get('latestEncounters')}")
print(f"Projects: {dashboard.get('projects')}")
print(f"Latest bulk import: {dashboard.get('latestBulkImportTask')}")

Available Query Helpers

The pywildbook.queries module provides these helper functions:

  • match_all() - Match all documents
  • filter_by_sex(sex) - Filter by sex
  • filter_by_species(species, genus=None) - Filter by species
  • filter_by_year_range(start_year, end_year) - Filter by year range
  • filter_by_date_range(start_date, end_date) - Filter by date range (ISO 8601)
  • filter_by_location(country, location_id, min_lat, max_lat, min_lon, max_lon) - Filter by location
  • filter_by_individual(individual_id) - Find encounters for an individual
  • filter_by_submitter(submitter_id) - Filter by submitter
  • text_search(field, text, fuzzy=False) - Text search in a field
  • field_exists(field) - Find documents where field exists
  • field_missing(field) - Find documents where field is missing
  • combine_queries(*queries, operator='must') - Combine multiple queries with AND/OR/NOT logic

Custom Queries

For advanced use cases, you can construct your own OpenSearch/Elasticsearch queries:

# Custom query using Elasticsearch DSL
custom_query = {
    'bool': {
        'must': [
            {'term': {'genus': 'Tursiops'}},
            {'range': {'year': {'gte': 2020, 'lte': 2023}}}
        ],
        'must_not': [
            {'term': {'sex': 'unknown'}}
        ]
    }
}

results = client.search_encounters(custom_query)

Error Handling

The client provides specific exceptions for different error scenarios:

from pywildbook import (
    WildbookClient,
    AuthenticationError,
    NotAuthenticatedError,
    NotFoundError,
    BadRequestError,
    ForbiddenError,
    APIError
)

client = WildbookClient('http://localhost:8080')

try:
    client.login('user@example.com', 'wrong_password')
except AuthenticationError as e:
    print(f"Login failed: {e}")

try:
    # Trying to search without logging in
    from pywildbook.queries import match_all
    results = client.search_encounters(match_all())
except NotAuthenticatedError as e:
    print(f"Not authenticated: {e}")

try:
    encounter = client.get_encounter('invalid-uuid')
except NotFoundError as e:
    print(f"Encounter not found: {e}")

Examples

See the examples/ directory for complete examples:

  • basic_usage.py - Basic login, search, and logout
  • advanced_search.py - Complex queries, pagination, and filtering
  • individual_statistics.ipynb - Analyze individual encounter patterns and statistics

Run examples:

# Set environment variables
export WILDBOOK_URL="http://localhost:8080"
export WILDBOOK_USERNAME="your@email.com"
export WILDBOOK_PASSWORD="yourpassword"

# Run basic example
uv run python examples/basic_usage.py

# Run advanced example
uv run python examples/advanced_search.py

# Install notebook dependencies, then open a notebook
uv sync --extra notebook
uv run jupyter notebook examples/individual_statistics.ipynb

Development

See CONTRIBUTING.md for contributor workflow, coding conventions, tests, and pull request guidance.

Setting up the development environment

# Clone the repository
git clone <repo-url>
cd pywildbook

# Initialize with uv
uv sync

# Run tests
uv run pytest

Notebook output stripping

nbstripout is configured to automatically strip cell outputs from .ipynb files before they are staged. After cloning, activate the git filter once:

uv run nbstripout install

After that, git add on any notebook will silently strip outputs before staging. Your local working copy keeps its outputs for interactive use; only clean notebooks are committed.

Project Structure

pywildbook/
├── src/
│   └── pywildbook/
│       ├── __init__.py          # Package exports
│       ├── client.py            # Main client class
│       ├── exceptions.py        # Custom exceptions
│       └── queries.py           # Query helper functions
├── examples/
│   ├── basic_usage.py
│   └── advanced_search.py
├── pyproject.toml               # Package configuration
└── README.md

API Reference

WildbookClient

Main client class for interacting with Wildbook.

Methods

  • __init__(base_url: str = None) - Create a new client instance (falls back to WILDBOOK_URL env var)
  • login(username: str, password: str) -> Dict - Authenticate user
  • logout() -> bool - End session
  • is_authenticated() -> bool - Check authentication status
  • get_current_user() -> Dict - Get current user info
  • get_user_home() -> Dict - Get user dashboard data
  • search_encounters(query, from_=0, size=10, sort=None, sort_order=None) -> Dict - Search encounters
  • get_encounter(encounter_id: str) -> Dict - Get specific encounter
  • search_individuals(query, from_=0, size=10, sort=None, sort_order=None) -> Dict - Search individuals
  • get_individual(individual_id: str) -> Dict - Get specific individual
  • filter_current_user() -> Dict - Query for encounters assigned to the logged-in user

Requirements

  • Python >= 3.11
  • requests >= 2.31.0

Optional: notebook extras

The individual_statistics.ipynb example requires additional dependencies. Install with:

uv sync --extra notebook

This adds ipykernel, ipyleaflet, notebook, and python-dotenv.

License

MIT License

Support

For issues, questions, and more information:

Related Projects

  • Wildbook - The main Wildbook platform
  • RWildbook - An R client with aligned functionality

Versioning

pywildbook and RWildbook follow a shared versioning convention to make feature equivalence explicit:

  • Major and minor versions are synchronised across both libraries. pywildbook 1.2.x and RWildbook 1.2.x expose the same API surface.
  • Patch versions are independent. Bug fixes, dependency updates, and other library-specific changes do not require a coordinated release.
  • Minor bumps are coordinated. When new features are added they land in both libraries together, then both get the minor bump.

This project follows Semantic Versioning.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pywildbook-1.0.1.tar.gz (11.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pywildbook-1.0.1-py3-none-any.whl (13.6 kB view details)

Uploaded Python 3

File details

Details for the file pywildbook-1.0.1.tar.gz.

File metadata

  • Download URL: pywildbook-1.0.1.tar.gz
  • Upload date:
  • Size: 11.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pywildbook-1.0.1.tar.gz
Algorithm Hash digest
SHA256 d64cc3ea04e384bcf4a79051c4407df662dffd55bda0144e0e5fe674a7f16f89
MD5 dab372f88a9aa66da1467e2808ba6562
BLAKE2b-256 f8c3ae3bee6de3c9d3600f9a591cd4d2a1c9417743cfb12c610d933c119be63b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pywildbook-1.0.1.tar.gz:

Publisher: publish.yml on WildMeOrg/pywildbook

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pywildbook-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: pywildbook-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 13.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pywildbook-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 215aaa4f9bf477ce1ec45d6e7406e056c36d8fe56b8b12ba295a79d31ae99705
MD5 9d530b564e00913a7e6778b227d4429e
BLAKE2b-256 bf427c635a6223b036a26eb2c8b5589f80378b0f0731f84c633b954b5594968c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pywildbook-1.0.1-py3-none-any.whl:

Publisher: publish.yml on WildMeOrg/pywildbook

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page