How to Send Us Your AMI Data
Last updated: October 15, 2026
What to export, in what format, so your meter data loads cleanly the first time.
Your AMI data is what lets Texture show meter locations, meter metadata, and interval usage. This guide covers what to send us, in what format, and the handful of details that save the most back-and-forth during onboarding. It doesn't cover customer or account information (name, email, phone, account number) — see How to Send Us Your MDM/CIS Data for that.
What to send
Send a manifest file. A small JSON file that applies to the whole delivery: your timezone (as an IANA name, not a raw offset), your sign convention, and your own counts — meter count, row count, total kWh — that we reconcile your files against before accepting the batch. It's also where fleet-wide defaults live (country, meter manufacturer) so you don't have to repeat them on every row.
Send your meters file. One row per meter: identity and site metadata — ID, status, location, interval length, manufacturer, address. See the field table below for what's minimum, preferred, and optional.
Send your interval/reads file. One row per meter per interval: timestamp, interval length, metric name, and value. This is what powers anything beyond meter and site metadata.
Use one of our accepted formats. A two-file split (a meters file plus an intervals file) is preferred. A single combined file — one row per reading with meter details repeated on every row — works for an initial historical backfill, but can't represent a meter that doesn't have readings yet, so it's not a fit for onboarding a meter ahead of its data. CSV is always fine; for large files, compress them, and for very large ones (tens of millions of rows), a columnar format like Parquet works well.
Send a full historical batch first, then ongoing batches for updates. A correction is a new batch, not an overwrite of a previous one — if something needs fixing, send it again rather than replacing the old file in place.
IMPORTANT Be explicit about what each value represents. State plainly whether a value is total energy consumed during the interval — not an average rate, and not a running meter reading. This matters more than it sounds: at 60-minute intervals, an energy total and a power rate are numerically identical, so a wrong assumption here can go completely unnoticed until the data is viewed at a different interval length. |
Required vs. optional fields
meters.csv — one row per meter. Minimum fields are what we need just to place and identify a meter; Preferred fields are strongly worth including; Optional fields add detail we can otherwise infer or live without.
Field | Tier | Notes |
|---|---|---|
meter_id | Minimum | Stable across batches. String — leading zeros are meaningful. |
status | Minimum | active / inactive. |
latitude, longitude | Minimum | 4 decimals minimum (~10 m), 5 preferred. Do not round on export. |
interval_minutes | Minimum | Per meter, not per file. Mixed fleets are normal. |
meter_manufacturer | Minimum | Strongly requested. Column, or a fleet default in the manifest. Absent from both → we infer, and the batch is sandbox-only. |
address_line1 | Preferred | Street address. |
city, state | Preferred | Derivable from coordinates if truly absent, but ask. |
postal_code | Preferred | String — ZIP+4 and leading zeros break under numeric coercion. |
address_line2 | Optional | Unit / suite / structure — its own field, not appended to line 1. |
service_point_id | Optional | Separates "meter swapped at one house" from "new house." |
site_key | Optional | Groups meters sharing a premise. Absent → we group by address + coordinates. |
site_name | Optional | Human label. |
premise_type | Optional | residential / commercial / industrial / agricultural / lighting / other. Absent → we infer from address text, less reliably. |
country | Optional | ISO 3166-1 alpha-2. Absent → US; declare defaults.country in the manifest otherwise. |
install_date, removal_date | Optional | Explains gaps that otherwise look like data loss. |
intervals.csv — one row per meter per interval. All five fields here are minimum — a reading is only usable with all of them.
Field | Tier | Notes |
|---|---|---|
meter_id | Minimum | Must match a meter_id in your meters file. |
interval_start | Minimum | ISO 8601 with a UTC offset — marks the start of the interval, not the end. Not a naive/local timestamp. |
interval_minutes | Minimum | 5, 15, 30, or 60. Can vary by meter, matching your meters file. |
metric | Minimum | Canonical name for what the value represents — don't assume it's implied by column position or file name. |
value | Minimum | Numeric, in the canonical unit for that metric. Zero is a real reading — never used as a filler for a missing interval. |
Good to know
A meters file alone is enough to get started. It's optional if you're only sending an update to existing interval data, but for onboarding, meters.csv is what lets us place and identify your fleet before any usage data arrives.
Don't zero-fill missing intervals. A real reading of zero is valid data and should be sent as 0. A missing interval should be left out entirely, not filled in with a zero — filling gaps this way makes missing data look like real (if surprising) usage.
An absent manufacturer keeps a batch in sandbox, not production. We can infer the manufacturer from the data, but we won't promote an inferred batch to production — device manufacturer drives too much downstream to guess silently. Set it as a column or as a manifest default.
Coordinate precision matters. Use at least 4 decimal places (roughly 10 meters), 5 preferred (roughly 1 meter) for dense areas. Don't round when you export — rounding is usually the actual source of imprecision, not a real limit on your data.
Corrections are new batches, not overwrites. If a batch needs fixing after the fact, send a corrected version as a new batch rather than replacing the original.