OpenPublica← The Record
▸ Contents
The Developer Wire● Free · No key

API Reference

The Open Publica API is free and needs no key. It powers this site: search and read US local-government meetings, pull AI summaries and full transcripts, and find which governments we cover near any place. Every route is a plain GET that returns JSON.

Your first request
curl 'https://api.openpublica.com/v1/meetings?limit=1'

Conventions

Envelope

Every success returns { data, meta } — both keys always present. data is the object or array you asked for; meta carries count, limit, page, and mode.

Discovery

GET /v1 with no path returns an index of every route, so the surface is browsable without leaving your terminal.

Dates & times

start_date and end_date are ISO YYYY-MM-DD, inclusive. Meetings carry both an ISO date and epoch_timestamp in Unix seconds (UTC).

Coordinates

Every coordinate is [longitude, latitude] — GeoJSON order, not lat/lng. That applies to near= and to a government's location.

Pagination

limit everywhere; page (1-based) where offset paging exists. meta.has_more tells you whether another page is waiting.

Lists

The gateway comma-joins repeated query keys, so only government_ids, fields, and near are parsed as comma-separated lists.

Compression

Send Accept-Encoding: gzip and anything over ~1 KB comes back gzipped. Listings are mostly prose and compress about 10x, so it is worth setting on any bulk read.

Sparse fields

On /v1/meetings, fields= names exactly what to return and nothing else is read. A full meeting is ~7 KB, so a 100-record page drops from ~700 KB to ~20 KB.

Errors

Failures return a matching HTTP status and a single-shape body: {"error": {"message": "..."}}. A missing entity is a real 404 rather than a 200 with a null payload, so you can branch on the status alone.

400
Bad request

A parameter is missing or malformed — no q on search, a non-ISO date, or more than one government selector.

404
Not found

Unknown meeting or government id, an unresolvable postal code, or a route that doesn't exist.

405
Method not allowed

v1 is GET-only. The response carries Allow: GET.

429
Too many requests

Throttled. The response carries Retry-After — wait that many seconds before retrying.

500
Server error

Something failed on our side. Retry with backoff.

The meeting object

Returned by every meeting route. Search results add scoring fields on top. Extra fields may appear over time — tolerate them.

Attributes
meeting_id
string

Canonical id. The only value the meeting routes accept.

government_id
string

The government this meeting belongs to.

title
string

Meeting title as filed.

date
string

Meeting date, ISO YYYY-MM-DD (UTC).

epoch_timestamp
integer

The same moment in Unix seconds — multiply by 1000 for a JS Date.

summary
string

The AI summary of proceedings, as markdown.

category_name
string

Human-readable meeting body, e.g. "City Council".

category_tag
string

Opaque UUID for that body — the value the category_tag filter takes.

topics
array

Objects of { topic, conversation_percentage } — the discussion breakdown.

video_url
string | null

Archived recording. Null until the deferred Internet Archive upload lands.

source_url
string

Original government link. The fallback for playback while video_url is null.

thumbnail_url
string

Still frame for the meeting.

transcript_url
string

JSON of timestamped transcript segments. Fetch it directly.

relevance_score
number

Semantic match strength. Search results only.

keyword_score
number

BM25 match strength. Keyword and hybrid results only.

highlights
array

Matched snippets. Keyword and hybrid results only.

The government object

Returned by every government route. A government is a city, county, or district whose meetings we track.

Attributes
government_id
string

Canonical id, e.g. "sacramento_county-california".

name
string

Display name, e.g. "Sacramento County, California".

city
string

City or county component of the name, lowercase.

state
string

State component, lowercase.

location
object

GeoJSON point — { type, coordinates: [longitude, latitude] }.

categories
array

Tracked meeting bodies: { name, tag, data_source, playlist_url }.

topics
array

Topic names seen across this government's meetings.

imageUrl
string

Government seal or icon. Note the camelCase — it predates the v1 renames.

weekly_summary
string

Markdown digest of the week. Only with include_weekly_summary=true.

The document object

Returned by the document routes. A document is one adopted publication — a general-plan element, a bound plan, or a budget — pinned to the government that adopted it. Which fields appear depends on status, so read that first.

Attributes
id
string

Document id — what the /v1/documents routes take. Search results spell it document_id.

government_id
string

The government that published it.

title
string

The document's own title, as published.

status
string

What this entry offers: converted, superseded, or failed. See below.

covers
array

Every plan slot this document answers for — a bound plan expands to all eight elements.

source_url
string

The government's own copy. Always present, including on a failed entry; cite it.

verified_on
string

Date we last confirmed the pin points at this document.

checked_at
string

Date we last looked at it.

page_count
integer

Pages in the source PDF. Absent on a failed document.

doc_type
string

Only on documents that carry one (budgets and the like); absent on general-plan elements.

availability
object

Present ONLY when something is wrong — its presence is the signal. Carries complete, reason, and problems.

superseded_by
string

The edition that retired this one. Only on a superseded document.

markdown_url
string

The whole document as markdown. Only on GET /v1/documents/{id}.

Meetings

List meetings

GET/v1/meetings

Returns a page of processed meetings, newest first. Scope to one government with government_id, or omit it to draw from every government at once.

Parameters
government_id
string

Scope to one government, e.g. "sacramento_county-california".

category_tag
string

Filter to one meeting body. An opaque UUID — read it from /v1/vocabulary rather than guessing.

topic
string

Only meetings that discussed this topic, e.g. "Affordable Housing".

start_date
string

Earliest meeting date, ISO YYYY-MM-DD (inclusive).

end_date
string

Latest meeting date, ISO YYYY-MM-DD (inclusive).

limit
integer

Results per page, 1–100. Defaults to 10.

page
integer

1-based page index. Defaults to 1; meta.has_more signals another page.

fields
string

Comma-separated sparse fieldset — each record comes back with only these fields. A full meeting is ~7 KB, so a bulk walk that needs a few scalars should say so.

curl 'https://api.openpublica.com/v1/meetings?government_id=sacramento_county-california&limit=5'
Response
{
  "data": [
    {
      "meeting_id": "6a5b0a986b2c35938e9a89aa",
      "government_id": "bellevue-washington",
      "title": "Bellevue City Council Meeting — July 17, 2026",
      "date": "2026-07-17",
      "epoch_timestamp": 1784313900,
      "category_name": "City Council",
      "category_tag": "c125a28d-4898-4276-ac9c-21050c767352",
      "summary": "# Bellevue City Council Meeting...",
      "video_url": "https://archive.org/download/.../meeting.mp4",
      "source_url": "https://bellevue.granicus.com/...",
      "thumbnail_url": "https://.../thumbnails/31127b60.webp",
      "transcript_url": "https://.../transcriptions/31127b60.json",
      "topics": [
        { "topic": "Economic Development", "conversation_percentage": 29 }
      ]
    }
  ],
  "meta": { "count": 5, "limit": 5, "page": 1, "has_more": true }
}

Search meetings

GET/v1/meetings/search

Searches every meeting summary. semantic matches by meaning, keyword matches exact terms such as ordinance numbers or names, and hybrid rank-fuses both. Semantic hits carry relevance_score; keyword and hybrid hits add keyword_score and highlights.

Parameters
q
stringRequired

The query — natural language, or exact terms in keyword mode.

mode
string

"semantic" (default), "keyword", or "hybrid".

government_id
string

Scope to one government.

government_ids
string

Comma-separated ids to search several governments at once. Overrides government_id.

category_tag
string

Meeting-body filter — an opaque UUID from /v1/vocabulary.

topic
string

Only meetings that discussed this topic.

min_score
number

Relevance floor for semantic matches, 0–1.

start_date
string

Earliest meeting date, ISO YYYY-MM-DD.

end_date
string

Latest meeting date, ISO YYYY-MM-DD.

limit
integer

Max results, 1–25. Defaults to 10.

curl 'https://api.openpublica.com/v1/meetings/search?q=affordable+housing&mode=hybrid&limit=5'
Response
{
  "data": [
    {
      "meeting_id": "6a5ba563566bda5aeed771be",
      "government_id": "san_antonio-texas",
      "title": "Public Hearing on FY2026 Affordable Housing Action Plan",
      "date": "2026-04-15",
      "epoch_timestamp": 1776211200,
      "summary": "# San Antonio City Council Public Hearing...",
      "video_url": null,
      "source_url": "https://sanantoniotx.new.swagit.com/videos/382391/download",
      "topics": [
        { "topic": "Affordable Housing", "conversation_percentage": 85 }
      ],
      "relevance_score": 0.7152,
      "keyword_score": 14.2758,
      "highlights": ["Affordable Housing"]
    }
  ],
  "meta": { "count": 5, "limit": 5, "match_type": "hybrid" }
}

Retrieve a meeting

GET/v1/meetings/{id}

Returns one meeting in full — the complete AI summary, topic breakdown, and the transcript_url holding the raw timestamped segments. Responds 404 if the id is unknown.

Parameters
id
string · pathRequired

The meeting_id from a list or search result.

curl 'https://api.openpublica.com/v1/meetings/6a5b0a986b2c35938e9a89aa'
Response
{
  "data": {
    "meeting_id": "6a5b0a986b2c35938e9a89aa",
    "government_id": "bellevue-washington",
    "title": "Bellevue City Council Meeting — July 17, 2026",
    "date": "2026-07-17",
    "epoch_timestamp": 1784313900,
    "category_name": "City Council",
    "summary": "# Bellevue City Council Meeting...",
    "video_url": "https://archive.org/download/.../meeting.mp4",
    "source_url": "https://bellevue.granicus.com/...",
    "transcript_url": "https://.../transcriptions/31127b60.json",
    "topics": [
      { "topic": "Historic Preservation", "conversation_percentage": 14 }
    ]
  },
  "meta": {}
}
Governments

Find governments

GET/v1/governments

Finds tracked governments three ways — pass at most one selector. q runs a name typeahead, near sorts by distance from a coordinate, and postal_code geocodes a ZIP then searches around it. With no selector you get an alphabetical page, which is what a city picker wants. meta.mode reports which selector ran.

Parameters
q
string

Name search, e.g. "sacramento" or "texas".

near
string

"lon,lat" — GeoJSON order, longitude first, e.g. -121.49,38.58.

postal_code
string

ZIP to geocode and search around. Unknown ZIP responds 404.

country_code
string

ISO country code for postal_code. Defaults to "US".

limit
integer

Max results, 1–50. Defaults to 10.

page
integer

1-based page index. Name and alphabetical modes only.

curl 'https://api.openpublica.com/v1/governments?postal_code=95814'
Response
{
  "data": [
    {
      "government_id": "sacramento_county-california",
      "name": "Sacramento County, California",
      "city": "sacramento county",
      "state": "california",
      "location": { "type": "Point", "coordinates": [-121.4418, 38.5692] },
      "categories": [
        {
          "name": "Board of Supervisors",
          "tag": "7e36f7b9-9156-42ba-a65b-7af71221626d",
          "data_source": "youtube"
        }
      ],
      "topics": ["Homelessness", "Affordable Housing"]
    }
  ],
  "meta": {
    "mode": "postal",
    "resolved_location": {
      "postal_code": "95814",
      "place_name": "Sacramento",
      "state_name": "California",
      "coordinates": [-121.4944, 38.5816]
    },
    "count": 1,
    "limit": 10
  }
}

Retrieve a government

GET/v1/governments/{id}

Returns one government's profile — display name, location, and the meeting bodies tracked for it. Ask for the AI weekly digest with include_weekly_summary. Responds 404 if the id is unknown.

Parameters
id
string · pathRequired

The government_id to look up.

include_weekly_summary
boolean

Pass true to include the markdown weekly digest.

curl 'https://api.openpublica.com/v1/governments/sacramento_county-california?include_weekly_summary=true'
Response
{
  "data": {
    "government_id": "sacramento_county-california",
    "name": "Sacramento County, California",
    "city": "sacramento county",
    "state": "california",
    "location": { "type": "Point", "coordinates": [-121.4418, 38.5692] },
    "weekly_summary": "## This week\nThe board approved..."
  },
  "meta": {}
}

Retrieve activity stats

GET/v1/governments/{id}/stats

Returns a meeting-activity rollup over a recent window: the total, a per-day histogram, and the most active meeting bodies. Pass "all" as the id for corpus-wide numbers.

Parameters
id
string · pathRequired

A government_id, or "all" for every government.

days
integer

Look-back window in days, 1–365. Defaults to 30.

curl 'https://api.openpublica.com/v1/governments/all/stats?days=7'
Response
{
  "data": {
    "government_id": "all",
    "total_meetings": 196,
    "activity_by_day": { "2026-07-13": 24, "2026-07-14": 68 },
    "categories": [
      {
        "name": "City Council",
        "count": 103,
        "tags": ["c125a28d-4898-4276-ac9c-21050c767352"]
      }
    ]
  },
  "meta": { "days": 7 }
}
Documents

Status & coverage

We hold adopted general plans and budgets as page-cited text. Two fields decide what a response actually claims, and both are easy to misread — worth a minute before calling the routes below.

status — what one document offers
converted
Served

Currently served: searchable, and readable by page range.

superseded
Retired

Replaced by a newer edition. Still stored and listable with include_superseded, but neither searchable nor readable.

failed
Unreadable

We hold this pin and could not read the document — NOT that the government published nothing. source_url still points at the city's own copy.

meta.coverage.state — whether we have looked at all
pinned
Documents pinned

We have pinned documents here. coverage.pinned_slots names which slots were looked for, which is not everything a city publishes.

reviewed_none_pinned
Looked, found none

We reviewed this government and pinned nothing.

unavailable
Could not review

Review was not possible; coverage.reason says why.

not_reviewed
Not looked at yet

We have not reviewed this government. Most tracked governments are here — an empty data is then a fact about OpenPublica, not about the city.

null
No row yet

The government was added since the coverage projection last ran. Not the same claim as not_reviewed, so don't collapse the two.

Coverage is what makes an empty data readable. Without it, “this government publishes nothing” and “we have never looked here” are the same response — and most tracked governments are the second.

covers, not element

A city that binds its whole general plan into one PDF gets pinned as combined, and that single document genuinely answers for all eight elements. covers names every slot it answers for; element, which appears on search hits, names only whichever pin acquired it. Read element alone and the rest of a city's plan looks unpublished.

List a government's documents

GET/v1/governments/{id}/documents

The adopted general-plan and budget documents held for one government. Read meta.coverage alongside data — it is what separates "this government publishes nothing" from "we have not looked here yet", and most tracked governments are the latter.

Parameters
id
string · pathRequired

The government_id to list documents for.

include_superseded
boolean

Pass true to include editions retired by a newer one. Defaults to false.

limit
integer

Max documents, 1–50 (also the default).

curl 'https://api.openpublica.com/v1/governments/san_antonio-texas/documents'
Response
{
  "data": [
    {
      "id": "9ad60b2d997f9c06b136ccfba64f3e2466589d59c22b3d66d2c7a9800a208186",
      "government_id": "san_antonio-texas",
      "title": "SA Tomorrow Comprehensive Plan (Adopted August 11, 2016)",
      "status": "converted",
      "covers": [
        "circulation", "combined", "conservation", "ej", "housing",
        "land_use", "noise", "open_space", "safety"
      ],
      "source_url": "https://sacompplan.com/new_docs/SA_CompPlan_FULLDoc.pdf",
      "verified_on": "2026-08-17",
      "checked_at": "2026-08-18",
      "page_count": 330
    }
  ],
  "meta": {
    "count": 1,
    "limit": 50,
    "coverage": {
      "state": "pinned",
      "pinned_slots": ["combined"],
      "seed_url": "https://sacompplan.com/",
      "reason": null,
      "last_reviewed": "2026-08-17"
    }
  }
}

Download the assembled general plan

GET/v1/governments/{id}/plan

Every served general-plan document for a government, assembled into one markdown file. Some cities bind their plan into a single PDF and others split it across eight, so this normalises them — asking for "the general plan" does not require knowing which kind of city you have. Sections run in the order a plan is conventionally bound (Gov Code §65302), not alphabetically. Budgets are excluded.

Returns raw text/markdown, not the { data, meta } envelope — the point of the route is to hand you a file. It 404s when no plan documents are served. Page anchors survive as <!-- page N --> but are per section, numbering that section's own source PDF.

Parameters
id
string · pathRequired

The government_id whose plan to assemble.

curl 'https://api.openpublica.com/v1/governments/alameda_county-california/plan'
Response · text/markdown
# General plan documents — alameda_county-california

Assembled by OpenPublica from the documents it serves for this
government. Each section below is a separate publication by the
government; page anchors (<!-- page N -->) refer to pages of that
section's own source PDF, linked in its heading.

**Contains 3 documents.**
OpenPublica has pinned these slots for this government: conservation,
ej, open_space. That is what we have looked for, not a statement that
the government publishes nothing else — a plan element absent here may
simply not have been reviewed yet. Last reviewed 2026-08-09.

---

## Conservation — Conservation Element

- Source: https://www.acgov.org/cda/planning/.../Conservation_Element.pdf
- Pages: 104
- Covers: conservation
- **Transcription findings were recorded for this document;** check
  figures against the source.

<!-- page 1 -->
# CONSERVATION ELEMENT
## OF THE
# ALAMEDA COUNTY GENERAL PLAN

Search document text

GET/v1/documents/search

Word search over page-cited chunks of currently-served documents. Each hit is an excerpt with the pages it came from, so it is citable on its own. text is a short passage around the match joined with …, not the whole page window — read it in context with the page route below.

This is word matching, not semantic search — unlike /v1/meetings/search there is no embedding behind it. Retired and unreadable documents are not indexed, so an empty result is not evidence the government published nothing; the listing route is what answers that.

Parameters
q
stringRequired

The words to find.

government_id
string

Scope to one government. Omit to search every served document.

limit
integer

Max excerpts, 1–50 (default 10).

curl 'https://api.openpublica.com/v1/documents/search?q=housing+element&limit=2'
Response
{
  "data": [
    {
      "document_id": "af906437578898f5447d3df23da1cf8d46607c548b98e2…",
      "government_id": "miami_dade_county-florida",
      "document_title": "III. Housing Element",
      "doc_type": null,
      "element": "housing",
      "first_page": 1,
      "last_page": 1,
      "document_complete": true,
      "source_url": "https://www.miamidade.gov/.../cdmp-housing-element.pdf",
      "text": "# HOUSING ELEMENT\n\n## Introduction\n\nThe purpose of the
                Housing Element is to provide a framework …",
      "score": 2.127356038305101
    }
  ],
  "meta": { "count": 2, "limit": 2 }
}

Retrieve a document

GET/v1/documents/{id}

One document's metadata — a listing entry plus markdown_url. The listing omits that URL because it is a fifth of an entry, and reading pages is what the route below is for.

Parameters
id
string · pathRequired

The document id, from a listing or search result.

curl 'https://api.openpublica.com/v1/documents/b5a2daeca5cc571e2b8867b6f77ac4ffe0c0802874e247b3688dcde9fda282c4'
Response
{
  "data": {
    "id": "b5a2daeca5cc571e2b8867b6f77ac4ffe0c0802874e247…",
    "government_id": "alameda_county-california",
    "title": "Conservation Element",
    "status": "converted",
    "covers": ["conservation"],
    "source_url": "https://www.acgov.org/.../Conservation_Element_1994.pdf",
    "verified_on": "2026-08-09",
    "checked_at": "2026-08-16",
    "page_count": 104,
    "availability": {
      "complete": false,
      "problems": [
        "5 footnote definition(s) never referenced (a marker may have been
         merged into a number)"
      ],
      "reason": "this document has transcription findings; any page that
                 could not be read is marked in place in the text"
    },
    "markdown_url": "https://openpublica.s3.us-west-2.amazonaws.com/….md"
  },
  "meta": {}
}

Read document pages

GET/v1/documents/{id}/text

The markdown of one page range. Pages are transcribed from rendered pixels rather than the PDF text layer, so scanned tables come through as text, and a page that could not be transcribed appears as a marked hole rather than a silent omission.

Cite covered, not requested. Text is assembled from the page windows overlapping your range, so covered runs wider when a window overran the range and narrower when the character bound cut it short. The span is clamped to 40 pages and 120k characters rather than rejected — asking for a whole 300-page plan gives you the start of it, not a 400.

Parameters
id
string · pathRequired

The document id to read.

first_page
integer

First page of the range. Defaults to 1.

last_page
integer

Last page. Defaults to first_page + 39, and is clamped to that span.

curl 'https://api.openpublica.com/v1/documents/b5a2daeca5cc571e2b8867b6f77ac4ffe0c0802874e247b3688dcde9fda282c4/text?first_page=1&last_page=2'
Response
{
  "data": {
    "document_id": "b5a2daeca5cc571e2b8867b6f77ac4ffe0c080…",
    "government_id": "alameda_county-california",
    "title": "Conservation Element",
    "source_url": "https://www.acgov.org/.../Conservation_Element_1994.pdf",
    "page_count": 104,
    "requested": { "first_page": 1, "last_page": 2 },
    "covered":   { "first_page": 1, "last_page": 6 },
    "markdown": "# CONSERVATION ELEMENT\n## OF THE\n# ALAMEDA COUNTY
                 GENERAL PLAN\n\n| | Volume |…"
  },
  "meta": { "max_page_span": 40 }
}
Discovery

List filter values

GET/v1/vocabulary

Returns the values the category_tag and topic filters accept: meeting bodies mapped to their opaque tag UUIDs, and the most common topic names, each with counts. Read this before filtering — category_tags cannot be guessed. Without dates it covers a recent window, echoed as meta.default_window_days.

Parameters
government_id
string

Scope to one government. Omit for the whole corpus.

government_ids
string

Comma-separated ids to scope to several governments.

start_date
string

Earliest meeting date, ISO YYYY-MM-DD.

end_date
string

Latest meeting date, ISO YYYY-MM-DD.

limit
integer

Max topics returned, 1–200. Defaults to 50.

curl 'https://api.openpublica.com/v1/vocabulary?government_id=sacramento_county-california'
Response
{
  "data": {
    "categories": [
      {
        "name": "Boards and Commissions",
        "count": 10,
        "tags": ["4d754a2b-3b8f-4706-9927-527fe66d0fb2"]
      }
    ],
    "topics": [
      { "topic": "Procedural", "count": 14 },
      { "topic": "Budget and Finance", "count": 11 }
    ]
  },
  "meta": { "total_meetings": 15, "default_window_days": 90, "limit": 50 }
}

Retrieve coverage totals

GET/v1/stats

Returns top-line counters for the whole archive — how many governments, states, and meetings Open Publica currently tracks. Takes no parameters.

Takes no parameters.

curl 'https://api.openpublica.com/v1/stats'
Response
{
  "data": {
    "total_governments": 103,
    "total_states": 47,
    "total_meetings": 5092
  },
  "meta": {}
}
MCPThe wire service · ● Live

Put the whole archive on your AI's desk.

Point Claude, Cursor, or any MCP-capable assistant at our endpoint and it can search every meeting, pull exact transcript quotes, and compare cities — live, with no scraping, account, or key.

Tools
find_governments_by_name

Resolve a city or state name to tracked governments.

find_governments_by_zip

Resolve a US ZIP code to nearby governments.

find_local_governments

Find governments near a coordinate.

discover_meeting_vocabulary

List the valid category and topic filter values.

search_meetings_semantic

Search all summaries — semantic, keyword, or hybrid.

list_recent_meetings

Browse a government's meetings, newest first.

get_meeting

Full summary, topics, and optionally the transcript.

search_meeting_transcript

Pull exact quotes from one transcript, with timecodes.

get_government_info

Profile and details for one government.

get_government_stats

Activity stats for a government or the whole corpus.

get_coverage_overview

Corpus totals — governments, states, meetings.

list_government_documents

A government’s adopted plan and budget documents, with its coverage state.

search_documents

Word search across plan and budget text, returning page-cited excerpts.

get_document

One document’s metadata.

read_document_pages

Read a page range of a document as markdown.

Endpoint
https://api.openpublica.com/mcp
mcp.json
{
  "mcpServers": {
    "openpublica": {
      "url": "https://api.openpublica.com/mcp"
    }
  }
}

In Claude web or desktop, add it under Settings → Connectors → Add custom connector.

Assembled plans are not an MCP tool. /plan returns a whole general plan — 27k to 179k tokens depending on the city — so it exists for download and offline analysis. The read path for a model is search_documents then read_document_pages, which is page-cited and bounded.

Notes & fair use
  • Archival upload is deferred, so video_url is null until a meeting's recording lands on the Internet Archive, then backfilled. Fall back to source_url meanwhile; summary, transcript, and thumbnail are always there.
  • Offered free for public-interest, research, and civic projects. Please cache responses and avoid hammering the endpoints.
  • Data comes from AI transcription and summarization and may contain errors — the official record is authoritative.
  • Shapes reflect current usage and may evolve. Build defensively and tolerate extra fields.
  • Need a city covered, or a higher-volume use case? Reach out.
Openpublica · Developer Wireopenpublica.com