Skip to main content

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.

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.2.1.tar.gz (193.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.2.1-py3-none-any.whl (59.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: archibald-1.2.1.tar.gz
  • Upload date:
  • Size: 193.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","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.2.1.tar.gz
Algorithm Hash digest
SHA256 9a68bbb4e25c5aae6d3c0da71cf9beddd882acd8427044de51f1a43de66725a2
MD5 f210c099f5eab31f42771fe811db8f3a
BLAKE2b-256 f768e143e4e16317d931e31fabcc13709d24612334be7b6b62b4331d7e9f6418

See more details on using hashes here.

File details

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

File metadata

  • Download URL: archibald-1.2.1-py3-none-any.whl
  • Upload date:
  • Size: 59.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","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.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f80119707a85d8ca0dddc81166aeea0ad63f506942604d6d1e09ffee45425799
MD5 f81df25e691e5979d40ea53bc5722786
BLAKE2b-256 aca629e5a7dcaf69b88dcf00f05c6604d167f2336d1a2a310978bba0ba885536

See more details on using hashes here.

Release history Release notifications | RSS feed

1.2.2

2 files

This release

1.2.1 This release

2 files

1.2.0

2 files

1.1.4

2 files

1.1.3

2 files

1.1.2

2 files

1.1.1

2 files

1.1.0

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page