Skip to content
Distribution · Cloudflare R2

PropRaven R2 delivery

Parquet files in a Cloudflare R2 bucket. Read them with any S3-compatible client: DuckDB, Polars, Spark, pandas or boto3. No warehouse is required, and R2 charges no egress.

Who can use it

Credentials are minted for the Developer and Team plans (and the grandfathered Business plan, team seats, and the API Pro and Scale plans). Free accounts get 402. You must be signed in; a signed-out call gets 401.

/pricing lists S3 (Parquet) delivery under the Data License, not under Developer or Team. This page describes what the credentials endpoint enforces today.

What's in the bucket

One bucket, propraven-data, organized by dataset and immutable version. Every release has a self-describing manifest.json with row counts, per-file SHA-256, and partition layout.

propraven-data/
├── _catalog.json                          ← top-level dataset registry
├── parcels-wide/                          ← 620-column wide parcels table
│   ├── _latest.txt
│   └── v=2026-09-06/
├── permits/                               ← per-parcel permit aggregates
│   └── v=2026-10-15/
└── parcels-silver/                        ← stale (see below)
    ├── v=2026-05-26/
    └── v=2026-05-23-ri-test/              ← Rhode Island test dataset

What _catalog.json lists as of 2026-10-15:

  • parcels-wide, latest v=2026-09-06: 620 columns, 51 states, 285.7M rows.
  • permits, v=2026-10-15: 159.9M rows.
  • parcels-silver, v=2026-05-26: stale. It has not been republished since May 2026.
  • parcels-silver/v=2026-05-23-ri-test is a test dataset (Rhode Island only, 1.77M rows). The examples below use it because it is small. Do not treat it as production data.

Get credentials

Credentials are short-lived and read-only, scoped to the bucket and the prefixes you request. Refresh before expiry; do not persist them in long-running config. Mint them on the R2 tokens settings page while signed in. The endpoint behind that page, POST https://propraven.com/api/v1/r2-credentials, authenticates with your signed-in session; it does not accept an API key yet.

  • Prefixes. prefixes may name only parcels-silver/, parcels-wide/, permits/ and _catalog.json. Omit it to get all four.
  • TTL. ttlSeconds is an integer from 60 to 43200 (1 to 720 minutes). The default is 43200 (12 hours).
  • 400 if prefixes is empty, not a list, or names a prefix outside that set (the response lists the allowed ones), if ttlSeconds is out of range, or if the body is not a JSON object.
  • 401 if you are not signed in. 402 if your plan does not include R2 delivery (Free).
  • 429 if you mint too often, 503 if issuance is busy or not configured, 502 if Cloudflare refuses the mint.
  • GET /api/v1/r2-credentials returns the bucket, the allowed prefixes, the TTL limits and whether your plan is entitled. It also needs a signed-in session.
POST https://propraven.com/api/v1/r2-credentials   (signed-in session)
Content-Type: application/json

{"prefixes": ["parcels-wide/", "_catalog.json"], "ttlSeconds": 43200}

# Response
{
  "bucket": "propraven-data",
  "endpoint": "https://<account_id>.r2.cloudflarestorage.com",
  "region": "auto",
  "accessKeyId":     "...",
  "secretAccessKey": "...",
  "sessionToken":    "...",   ← required (these are STS-style temp creds)
  "prefixes":        ["parcels-wide/", "_catalog.json"],
  "expires_at":      "2026-05-25T06:51:00Z",
  "ttl_seconds":     43200
}

DuckDB quickstart

INSTALL httpfs; LOAD httpfs;
SET s3_endpoint='<account_id>.r2.cloudflarestorage.com';
SET s3_access_key_id='<accessKeyId>';
SET s3_secret_access_key='<secretAccessKey>';
SET s3_session_token='<sessionToken>';
SET s3_url_style='path';

-- 1. Discover what's published
SELECT * FROM read_json_auto('s3://propraven-data/_catalog.json');

-- 2. Read a dataset version directly (parcels-wide, 620 columns).
--    hive_partitioning=false: the files carry their own state column; a
--    state=NN folder name is a FIPS code DuckDB would otherwise put in its place.
SELECT count(*)
FROM read_parquet('s3://propraven-data/parcels-wide/v=2026-09-06/**/*.parquet', hive_partitioning=false);

-- 3. Spatial example on the Rhode Island TEST dataset (geometry is WKB, EPSG:4326)
INSTALL spatial; LOAD spatial;
SELECT prpv_parcel_id, ST_Area(ST_GeomFromWKB(geometry)) AS sqdeg
FROM read_parquet('s3://propraven-data/parcels-silver/v=2026-05-23-ri-test/state=44/*.parquet', hive_partitioning=false)
LIMIT 5;

Python (boto3)

import boto3, json

# 1. Mint credentials on https://propraven.com/settings/r2-tokens (signed in)
#    and save the JSON response next to this script.
creds = json.load(open("r2-credentials.json"))

# 2. Open S3 client against R2
s3 = boto3.client(
    "s3",
    endpoint_url=creds["endpoint"],
    aws_access_key_id=creds["accessKeyId"],
    aws_secret_access_key=creds["secretAccessKey"],
    aws_session_token=creds["sessionToken"],
    region_name="auto",
)

# 3. Read a manifest to discover files (this is the Rhode Island TEST dataset)
m = json.loads(s3.get_object(
    Bucket="propraven-data",
    Key="parcels-silver/v=2026-05-23-ri-test/manifest.json",
)["Body"].read())
print(m["row_count_total"], "rows across", m["state_count"], "states")

Manifest schema

Every v=YYYY-MM-DD/manifest.json is self-describing. Validate file integrity with the per-file SHA-256.

{
  "dataset": "parcels-silver",
  "version": "2026-05-23-ri-test",
  "release_id": "parcels-silver/v=2026-05-23-ri-test",
  "schema_version": 1,
  "publisher": "PropRaven",
  "license": "Commercial — see https://propraven.com/terms",
  "format": "parquet",
  "compression": "snappy",
  "partitioning": ["state_fips"],
  "geometry_format": "WKB",
  "geometry_crs": "EPSG:4326",
  "row_count_total": 1775033,
  "state_count": 1,
  "states": [{
    "state_fips": "44",
    "rows": 1775033,
    "files": [{
      "path": "state=44/part-00000.parquet",
      "size_bytes": 646553288,
      "sha256": "22ad8c5079bb...",
      "rows": 1775033
    }]
  }],
  "source_attribution": ["...", "..."],
  "created_at": "2026-05-24T18:48:08+00:00"
}

Refresh cadence

  • parcels-wide: a new version is published when a national parcel build is copied to this bucket. The latest here is v=2026-09-06; the build currently served by the API is shown on /data/live.
  • permits: republished as the permit aggregates are rebuilt (latest v=2026-10-15).
  • parcels-silver: not republished since 2026-05-26.
  • Each new version is published under its own v=YYYY-MM-DD prefix. Old versions remain readable until the publisher GC'd (we keep at least the last 4).
  • _catalog.json and _latest.txt point to the current version — read those first; cache for 60s if you re-poll.

What this isn't

  • Not a real-time API. For change events, use webhooks.
  • Not row-level filtered. The whole partition file is what you get.
  • Not for embedding in a public app — these are short-lived backend credentials.
  • Not a metered download. R2 charges no egress.