Free check ·

Replace a Socrata rows.csv link without breaking your columns

Tyler Technologies removes Socrata's SODA1 API on December 16, 2026, and scripts that download /api/views/…/rows.csv?accessType=DOWNLOAD stop working. One public pipeline already reported HTTP 410 feature_deprecated in October. Paste your link to get a replacement that keeps the old column names and keeps numbers as numbers.

Check a dataset

Or try one:

No sign-in. Your browser asks the portal you name for its public column list and the first line of the old download. What you paste is not sent to CyberNative.

One salary, three downloads

Tyler's SODA3 guide maps the full download to export.csv. The same article says to use query.csv for machine processing. Here is one employee's annual salary from Chicago's salary dataset, downloaded each way on October 9, 2026:

EndpointHeaderValuepandas reads
rows.csvSODA1, being removedAnnual Salary165624.00number
export.csvthe documented swapAnnual Salary"$165,624.00"text
query.csvfor machine processingannual_salary"165624"number, new name
Same row in each file. export.csv keeps the names and formats the numbers. query.csv keeps the numbers and renames the columns. The checker combines the two good halves.

In a sample of 120 datasets drawn from the 1,000 most-viewed in Socrata's US catalog, export.csv turned at least one plain-number column into formatted text in 65. query.csv kept the numbers in all 120, but it renamed columns in 115. This is a popular-dataset sample from one run on October 9, 2026, executed by us and not independently reproduced.

No page needed: a general helper

This function does the core of the fix for any dataset. It reads the column list, downloads query.csv and renames the columns. Unlike the snippets above, it does not restore dates or integer types. Pass regions=True (in R, regions = TRUE) if your old file also had the region columns the portal adds from a location: San Francisco's files did, New York City's and Chicago's did not.

import json
import urllib.request

import pandas as pd


def read_socrata(portal, dataset, app_token=None, regions=False):
    """Read a Socrata dataset with its SODA1 column names and raw values.

    regions=True keeps the ":@computed_region_" columns, for portals whose
    old file had them.
    """
    with urllib.request.urlopen(f"https://{portal}/api/views/{dataset}.json") as r:
        cols = [c for c in json.load(r)["columns"]
                if (not c["fieldName"].startswith(":")
                    or regions and c["fieldName"].startswith(":@computed_region_"))
                and "hidden" not in (c.get("flags") or [])]
    df = pd.read_csv(
        f"https://{portal}/api/v3/views/{dataset}/query.csv",
        storage_options={"X-App-Token": app_token} if app_token else None,
    )
    return df[[c["fieldName"] for c in cols]].rename(columns={c["fieldName"]: c["name"] for c in cols})
read_socrata <- function(portal, dataset, app_token = NULL, regions = FALSE) {
  meta <- jsonlite::fromJSON(sprintf("https://%s/api/views/%s.json", portal, dataset))
  cols <- meta$columns
  hidden <- if (is.null(cols$flags)) FALSE else vapply(cols$flags, function(f) "hidden" %in% f, TRUE)
  keep <- !startsWith(cols$fieldName, ":") |
    (regions & startsWith(cols$fieldName, ":@computed_region_"))
  cols <- cols[keep & !hidden, ]
  path <- tempfile(fileext = ".csv")
  download.file(sprintf("https://%s/api/v3/views/%s/query.csv", portal, dataset), path, quiet = TRUE,
                headers = if (!is.null(app_token)) c("X-App-Token" = app_token))
  df <- read.csv(path, check.names = FALSE)[cols$fieldName]
  names(df) <- cols$name
  df
}

How we checked this page

On October 9, 2026 we ran the page's pandas and R snippets on 17 public datasets from 12 portals: New York City, Chicago, the CDC, New York State, New York State Health, Washington State, Austin, Cambridge, Edmonton, the Los Angeles Controller, Colombia's national portal and San Francisco. We compared each result with the real SODA1 file, read by pandas.read_csv and base R's read.csv with default settings.

  • Column names and order matched the old file in all 17, in both languages. That includes the region columns San Francisco's old files had and New York City's and Chicago's did not: the page reads the old file's first line while it still answers and follows it.
  • On the 12 datasets we downloaded in full (up to 368,102 rows), row counts matched, every column the old file gave as numbers stayed numeric, and the values matched. The exceptions are columns the checker lists under step 05: rounding the old file applied, and location text.
  • On the 5 largest, we compared the first 40 MB of each file. Names and pandas column types matched. Rows come back in a different order, so we did not compare values there.

pandas 3.0.6 and R 4.4.2. This was executed once by us and not independently reproduced.

Limits

  • This works for public tables. Private datasets, filtered views and map or chart views are not covered.
  • Once the old download stops answering, the page can no longer check which columns it had. It then keeps the portal's region columns in lines marked for deletion: delete them if your old file did not have them.
  • Rows can come back in a different order. Sort them if your code relies on order.
  • The export warning comes from each column's display format and its smallest and largest values. On the 120-dataset sample it flagged 290 of the 291 columns that broke there. It also flagged 22 that had not broken in the part of each file we compared.
  • CyberNative is not affiliated with Tyler Technologies or Socrata. Their documentation is the authority on dates and endpoints: Deprecation Roadmap.