Skip to main content

A lightweight library for interacting with ESRI ArcGIS REST APIs.

Project description

archibald

PyPI Python License Docs

An async Python client for interacting with ESRI ArcGIS REST APIs, designed around a dataframe-first approach for seamless analysis and data editing with pandas and geopandas.

Why archibald?

Esri's official arcgis Python library is comprehensive, but it comes with tradeoffs that make it difficult to use in modern data engineering contexts. archibald is/has:

  • Async first. Every call blocks in arcgis, which makes concurrent operations across multiple layers awkward, expensive, and time-consuming. archibald is async-native throughout.
  • Light. The arcgis package pulls in hundreds of megabytes of dependencies — map rendering, notebook widgets, spatial analysis services, and more. archibald's dependency surface is httpx, pandas, and geopandas. Ideal for AWS Lambdas and other contexts where bloat comes at a premium.
  • Clear error handling. Esri's API frequently returns HTTP 200 responses with embedded error bodies that the official library doesn't always surface. archibald treats these as first-class exceptions with a typed hierarchy.
  • Robust DataFrame integration. The official library's SpatialDataFrame type has historically been finicky. archibald deals entirely in pd.DataFrame and gpd.GeoDataFrame objects.
  • Editing that meets you at the DataFrame. Pass a GeoDataFrame and archibald handles the rest: geometry serialization, field type coercion, and validation before anything hits the wire. Write operations range from fine-grained apply_edits() to a full DataFrame sync(), all returning structured results with per-operation failure details.

archibald is deliberately narrow in scope: FeatureServer and MapServer interactions, done well. It is not a replacement for the full arcgis SDK if you need portal management, hosted notebooks, or Esri's spatial analysis services.

Installation

pip install archibald

Or with uv:

uv add archibald

Quick Start

import archibald as arc

auth = arc.UserTokenAuth(
    username="your_username",
    password="your_password",
)

async with arc.ArchieClient(
    base_url="https://services.arcgis.com/sharing/rest/services",
    auth=auth,
) as client:
    layer = arc.FeatureLayer(
        client=client,
        service_path="MyService/FeatureServer",
        layer_id=0,
    )

    # Query features and convert to a pandas DataFrame
    result = await layer.query(where="1=1")
    df = result.to_frame()

    # Or work with spatial data as a GeoDataFrame
    gdf = result.to_geodataframe()

    # Bulk insert from a DataFrame
    edits = await layer.append(df.head(5).copy())

Authentication

NoAuth — for public feature layers:

auth = arc.NoAuth()

UserTokenAuth — for token-based authentication:

auth = arc.UserTokenAuth(
    username="your_username",
    password="your_password",
    base_url="https://www.arcgis.com",  # optional; defaults to ArcGIS Online
)

Custom auth — implement the ArcGISAuth abstract base class to plug in your own token strategy (API keys, OAuth2, keyring-backed credentials, etc.):

class MyCustomAuth(arc.ArcGISAuth):
    async def get_token(self) -> str:
        # your token logic

    async def force_refresh(self) -> None:
        # refresh logic

Services

archibald wraps Esri's REST endpoints with typed service and layer classes:

Class Description
FeatureLayer Query, add, update, delete, upsert, and sync features; attach, update, and delete attachments
MapLayer Query-only access to map service layers
FeatureService Service-level metadata and capabilities
MapService Map service metadata and operations

All layers expose:

  • query() — retrieve features with optional filtering and field selection
  • fields() — introspect layer schema
  • crs() — coordinate reference system metadata
  • query_attachments() — list attachment metadata for one or more features

FeatureLayer additionally supports a layered write API:

  • apply_edits() — fine-grained control over add/update/delete in a single call
  • append() — bulk insert from a DataFrame
  • upsert() — insert or update based on key fields
  • sync() — reconcile a DataFrame with the service: adds missing records, updates changed ones, deletes removed ones
  • add_attachments() — attach files to one or more features
  • update_attachments() — replace existing attachment files
  • delete_attachments() — remove attachments from features

Data Models

QueryResult — returned by query():

result = await layer.query(where="population > 10000")

df = result.to_frame()          # pandas DataFrame
gdf = result.to_geodataframe()  # GeoDataFrame (when geometry is present)

# Access raw features and field metadata
features = result.features
fields = result.fields

ApplyEditsResult — returned by apply_edits(), append(), upsert(), and sync():

edits = await layer.apply_edits(adds=[...], updates=[...], deletes=[...])

if edits.has_failures:
    print("Failed adds:", edits.failed_adds)
    print("Failed updates:", edits.failed_updates)
    print("Failed deletes:", edits.failed_deletes)

FieldsResult — layer schema information:

fields = await layer.fields()

field_names = fields.names
numeric_fields = fields.filter(
    types=["esriFieldTypeSmallInteger", "esriFieldTypeInteger"]
)
df = fields.to_frame()

AttachmentsQueryResult — returned by query_attachments():

result = await layer.query_attachments(definition_expression="status = 'open'")

df = result.to_frame()                          # camelCase column names
df = result.to_frame(use_field_names=True)      # ESRI attachment-table field names

AttachmentsResult — returned by add_attachments(), update_attachments(), and delete_attachments():

result = await layer.add_attachments(
    object_ids=42,
    files=Path("photo.jpg"),
)

if result.has_failures:
    print("Failed attachments:", result.failed)

df = result.to_frame()

All three attachment write methods accept the same three calling modes:

  • Single — one object ID and one file
  • Fan-out — one object ID and multiple files (all attached to the same feature)
  • Multi — parallel iterables of object IDs and files (one file per feature, run concurrently)

Error Handling

archibald raises typed exceptions for both Esri service errors and client-side errors, including the common case of HTTP 200 responses that contain embedded error payloads.

try:
    result = await layer.query(where="invalid syntax")
except arc.ArcGISError as e:
    print(f"Error {e.code}: {e.message}")

The ArcGISError hierarchy:

Exception When it's raised
TokenExpiredError Token has expired (archibald auto-refreshes and retries)
TokenMissingError Token required but not present
AuthorizationError Insufficient permissions for the operation
NotFoundError Resource not found
ServiceError Other Esri service errors

ArchieClientError and its subclasses cover archibald-originated errors (invalid parameters, unsupported capabilities, etc.).

Roadmap

  1. Geocoding operations — suggest and batch geocode endpoints

Contributing

See CONTRIBUTING.md for guidance on setting up a development environment, running tests, and submitting pull requests.

Changelog

See CHANGELOG.md for release notes and version history.

License

MIT — see LICENSE for details.

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

archibald-1.1.2.tar.gz (187.0 kB view details)

Uploaded Source

Built Distribution

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

archibald-1.1.2-py3-none-any.whl (56.1 kB view details)

Uploaded Python 3

File details

Details for the file archibald-1.1.2.tar.gz.

File metadata

  • Download URL: archibald-1.1.2.tar.gz
  • Upload date:
  • Size: 187.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for archibald-1.1.2.tar.gz
Algorithm Hash digest
SHA256 bb44e6c8cf7c1a850e96c4855779d7138cf4fccbb9a46242862bee9bcad6231e
MD5 17c87128a9599c02ea2fa6824ba5a833
BLAKE2b-256 d0b7a9ab61871173a37ca2da9710ee0b6a5488508a7ed8ef7575cdc05cf6ca88

See more details on using hashes here.

File details

Details for the file archibald-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: archibald-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 56.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for archibald-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 69c012f2ca7db3f993b47482590d0e8642f01ce5ecd69d2ceed23172abda1708
MD5 4827a1d43ece24189c4510dd2bc2ef9d
BLAKE2b-256 2f34adc0d7307a5a97502d557f9213509eba3a43f089917234d9a8e4f5e10d75

See more details on using hashes here.

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