PropRaven R2 delivery
Egress-free Parquet. PropRaven publishes the canonical datasets to a Cloudflare R2 bucket. Read them with any S3-compatible client — DuckDB, Polars, Spark, pandas, boto3 — no warehouse required and no per-byte egress charge if your compute also runs on Cloudflare or Hetzner.
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-silver/
│ ├── _latest.txt ← "v=2026-05-23-ri-test"
│ └── v=2026-05-23-ri-test/
│ ├── manifest.json
│ └── state=44/part-00000.parquet
├── parcels-wide/ ← 26-col wide PE snapshot
└── permits/ ← per-parcel permit aggregatesActive today (beta): Rhode Island parcels (1.77M rows). California (18.29M) is staged and unlocks once the request flow opens. National rollout (191.3M parcels) lands on the same publication cadence as the Snowflake share.
Get credentials
R2 access is part of the Pro and Scale plans. Credentials are short-lived (12-hour TTL, scoped read-only to the bucket and the prefixes you request). Refresh before expiry — do not persist them in long-running config.
curl -X POST https://api.propraven.com/v1/r2-credentials \
-H "Authorization: Bearer $PROPRAVEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prefixes": ["parcels-silver/", "_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-silver/", "_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
SELECT count(*)
FROM read_parquet('s3://propraven-data/parcels-silver/v=2026-05-23-ri-test/state=*/*.parquet');
-- 3. Spatial example (geometry column 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')
LIMIT 5;Python (boto3)
import boto3, requests
# 1. Mint credentials
creds = requests.post(
"https://api.propraven.com/v1/r2-credentials",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"prefixes": ["parcels-silver/"], "ttlSeconds": 43200},
).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 the manifest to discover files
import json
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-silver— published on each ontology release (same cadence as the Snowflake share, ~biweekly).parcels-wide— mid-month wide snapshot.permits— daily reagg.- Each new version is published under its own
v=YYYY-MM-DDprefix. Old versions remain readable until the publisher GC'd (we keep at least the last 4). _catalog.jsonand_latest.txtpoint 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 free egress unless your compute is on Cloudflare, Hetzner, or another cloud that peers with Cloudflare. R2 itself has zero egress charges; your compute provider may still bill ingress.