Database Replica
:::tip Enterprise Feature Access to a fully isolated, daily-updated database replica is available for enterprise clients. This is not included with standard API access.
The replica enables you to fully ingest and customize data for your own use cases. It is powered by logical replication through NeonDB Postgres.
Contact sales@midpage.ai to learn more about enterprise access. :::
Connect to the Midpage read replica using PostgreSQL.
Connection String
postgresql://user:password@host:port/database
Contact us to obtain your connection credentials.
Connecting with psql
psql "postgresql://user:password@host:port/database"
Connecting with Python
import psycopg2
conn = psycopg2.connect(
host="host",
port="port",
database="database",
user="user",
password="password"
)
cursor = conn.cursor()
cursor.execute("SELECT * FROM opinions.courts LIMIT 10")
rows = cursor.fetchall()
Connecting with Node.js
import { Pool } from 'pg'
const pool = new Pool({
connectionString: process.env.DATABASE_URL
})
const result = await pool.query('SELECT * FROM opinions.courts LIMIT 10')
console.log(result.rows)
Schema Access
The database contains two schemas:
opinions— Court opinions and caselawlaws— Statutes and regulations
-- List all tables in opinions schema
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'opinions';
-- List all tables in laws schema
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'laws';
See the Schema Reference for complete table documentation.
Change Tracking
All tables include an updated_at column with automatic triggers. This timestamp is updated whenever a row is modified, helping you track changes made on our end.
-- Find recently updated docket entries
SELECT id, description, updated_at
FROM cases.docket_entries
WHERE updated_at > NOW() - INTERVAL '7 days'
ORDER BY updated_at DESC;
This is useful for incremental syncs—you can query for rows where updated_at is greater than your last sync timestamp.
One case updated_at does not cover
Incremental sync on updated_at catches inserts, edits and deletions — but not
withdrawals.
-
A deletion sets
deleted_atand bumpsupdated_at. Your next incremental query sees the row and its tombstone, and you can mark it deleted on your side. -
A withdrawal — where a case or document stops being part of the published
corpus, for example because it was removed at a court's request or found to
be a duplicate — removes the row outright. It leaves no tombstone, and
because the row is gone there is nothing for an
updated_atquery to return.
So a sync built only on updated_at will accumulate rows that are no longer in
our corpus. Withdrawals are uncommon, but they are not rare enough to ignore:
plan on a periodic full-key reconciliation — pull the complete set of IDs for a
table and drop anything on your side that is no longer present.
-- Reconciliation key set: every live case we currently publish
SELECT id FROM cases.cases WHERE deleted_at IS NULL;
Monthly is enough for most consumers. If you need withdrawals reflected faster than that, talk to us — we can tell you which tables actually move.