muriel 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
muriel-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Muriel AI, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ include requirements.txt
4
+ recursive-include muriel *.py
5
+ recursive-exclude * __pycache__
6
+ recursive-exclude * *.py[co]
muriel-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.4
2
+ Name: muriel
3
+ Version: 0.2.0
4
+ Summary: Muriel: ask the U.S. drinking water record in plain English (agent-first SDK)
5
+ Author-email: "Muriel AI, Inc." <info@murielai.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://murielai.io/developers
8
+ Project-URL: Documentation, https://docs.murielai.io
9
+ Project-URL: API reference, https://api.murielai.io/docs
10
+ Project-URL: Repository, https://github.com/Muriel-AI-Inc/muriel-sdk-python
11
+ Project-URL: Bug Tracker, https://github.com/Muriel-AI-Inc/muriel-sdk-python/issues
12
+ Keywords: water,quality,api,pfas,lead,contaminants,environmental
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: requests>=2.31.0
29
+ Requires-Dist: pydantic>=2.0.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
33
+ Requires-Dist: black>=23.0.0; extra == "dev"
34
+ Requires-Dist: isort>=5.12.0; extra == "dev"
35
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
36
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ # Muriel Python SDK
40
+
41
+ [Muriel](https://murielai.io) is the AI agent for U.S. drinking water data. It
42
+ answers plain-English questions about any public water system from the
43
+ monitoring results states publish, checks them against federal limits, and
44
+ shows the records behind every answer.
45
+
46
+ There are three ways to use it, and this README covers them in the order most
47
+ people need them:
48
+
49
+ 1. **[In your AI assistant](#1-use-muriel-in-your-ai-assistant-mcp-connector)**:
50
+ add the Muriel connector to Claude, ChatGPT, Cursor, VS Code or any MCP
51
+ client and ask in the chat you already use. No code, no key.
52
+ 2. **[The agent, from code](#2-ask-the-agent-from-code)**: `muriel.query("...")`
53
+ sends a question to the same agent and returns the answer, the records it
54
+ used, and any CSV it produced, shaped the way your program needs.
55
+ 3. **[The REST API](#3-query-the-rest-api-directly)**: exact, parameter-driven
56
+ queries for one system's history, one contaminant nationwide, every system
57
+ in a state, or the system serving an address. Use this package's functions or
58
+ call it over HTTP from any language.
59
+
60
+ All three draw on the same data and the same Muriel account and credits.
61
+
62
+ ## What Muriel covers
63
+
64
+ - **88 million+ results** from **32 states** and **63,000+ water systems**
65
+ - **Current:** many states update daily, often within days of sampling
66
+ - **Deep:** some states go back to the 1970s
67
+ - **Every contaminant states test for:** lead and copper, PFAS, nitrate,
68
+ disinfection byproducts, arsenic, coliform, VOCs and more
69
+
70
+ Every answer is built from the sample results themselves, not from summaries or
71
+ annual reports, and says how current each state's data is. A gap is never
72
+ passed off as a clean record. See exactly which states, contaminants and years
73
+ are covered, live, at [murielai.io/overview](https://murielai.io/overview).
74
+
75
+ Muriel is built for people who work with drinking water: utilities and the
76
+ regulators who oversee them, and the labs, engineering firms and vendors who
77
+ serve them.
78
+
79
+ ---
80
+
81
+ ## 1. Use Muriel in your AI assistant (MCP connector)
82
+
83
+ The quickest way to put Muriel to work. Add one URL to your assistant, sign in
84
+ to your Muriel account and click **Allow**. There is no API key to copy and
85
+ nothing to install.
86
+
87
+ **Connector URL:** `https://mcp.murielai.io/mcp`
88
+
89
+ | Assistant | How to add it |
90
+ |---|---|
91
+ | **Claude** (claude.ai and desktop) | Settings → Connectors → *Add custom connector* → name it Muriel, paste the URL → *Connect* |
92
+ | **Claude Code** | `claude mcp add --transport http --scope user muriel https://mcp.murielai.io/mcp`, then `/mcp` → muriel → *Authenticate* |
93
+ | **Cursor** | In `~/.cursor/mcp.json`: `{"mcpServers": {"muriel": {"url": "https://mcp.murielai.io/mcp"}}}` |
94
+ | **VS Code** (Copilot agent mode) | In `.vscode/mcp.json`: `{"servers": {"muriel": {"type": "http", "url": "https://mcp.murielai.io/mcp"}}}` |
95
+ | **ChatGPT** | Where custom connectors are enabled: Settings → Connectors → create one with the URL and OAuth |
96
+ | **Anything else** | Any client that supports remote MCP servers with OAuth takes the URL. For local-only (stdio) clients: `npx -y mcp-remote https://mcp.murielai.io/mcp` |
97
+
98
+ The first time your assistant uses Muriel it opens a Muriel page: sign in if
99
+ you need to, see what it is asking for, and click **Allow**. To disconnect,
100
+ use *Connected apps* on your [account page](https://murielai.io/account).
101
+
102
+ Then just ask, in the chat you already use:
103
+
104
+ > Anything new with Chicago's water this week?
105
+ >
106
+ > Which systems in Georgia had a nitrate result above the MCL in the last year?
107
+ >
108
+ > Where has PFOA or PFOS been detected above 4 ppt in Pennsylvania?
109
+
110
+ Your assistant calls Muriel whenever a question needs drinking water data and
111
+ answers from what Muriel returns. The connector gives it 16 read-only tools,
112
+ the same ones Muriel's own agent uses:
113
+
114
+ | Tools | What they answer |
115
+ |---|---|
116
+ | `find_water_system` | A utility or town name → its water system ID (PWSID) |
117
+ | `get_sample_results`, `summarize_sample_results`, `deep_history_search` | Individual results and summaries, current and archived |
118
+ | `get_notable_events`, `get_record_changes` | What changed in the latest updates |
119
+ | `analyte_trend` | Whether a contaminant is going up or down |
120
+ | `check_compliance_from_data`, `check_monitoring_gaps` | Running annual averages, 90th percentiles and missing samples |
121
+ | `rank_systems_by_size` | State-wide questions with no system named |
122
+ | `data_coverage`, `historical_depth` | What data exists for a state or system, and how far back |
123
+ | `lookup_standard`, `evaluate_result`, `resolve_analyte`, `search_regulations` | The federal standards and rules |
124
+
125
+ The connector can't see your Muriel conversations or documents, change your
126
+ account or buy anything. Full reference:
127
+ [murielai.io/developers](https://murielai.io/developers#connector).
128
+
129
+ ---
130
+
131
+ ## 2. Ask the agent from code
132
+
133
+ Install the package and set your key. Create a key on your
134
+ [account page](https://murielai.io/account).
135
+
136
+ ```bash
137
+ pip install muriel
138
+ export MURIEL_API_KEY="muriel_live_..."
139
+ ```
140
+
141
+ Every function reads `MURIEL_API_KEY`; you can also pass `api_key=` directly.
142
+
143
+ ```python
144
+ import muriel
145
+
146
+ answer = muriel.query("Is Chicago's water meeting federal limits for lead and copper?")
147
+
148
+ print(answer.summary) # the answer, in plain English
149
+ for r in answer.results[:5]: # the records it rests on
150
+ print(r["sample_collection_date"], r["system_name"],
151
+ r["result_analyte_name"], r["result_value"], r["result_unit"])
152
+ ```
153
+
154
+ `muriel.query()` asks the same agent that answers at
155
+ [murielai.io](https://murielai.io). It works out which systems, contaminants and
156
+ dates the question is about, pulls the results, compares them with MCLs, action
157
+ levels and treatment rules, and tells you when a state's data is out of date.
158
+ Each answer carries:
159
+
160
+ | Field | What it holds |
161
+ |---|---|
162
+ | `summary` | The answer in plain English |
163
+ | `results`, `count` | The records the answer rests on |
164
+ | `documents` | Any CSV or PDF the agent produced, with a download URL |
165
+ | `warning` | A caveat, when the data needs one |
166
+ | `execution_log` | The steps the agent took |
167
+ | `credits` | What the question used, and your balance after it |
168
+
169
+ ### What you can ask
170
+
171
+ | | |
172
+ |---|---|
173
+ | **Compliance** | Which systems in Georgia had a nitrate result above the MCL in the last year? |
174
+ | **Trends** | Which systems are within 20% of an MCL and trending up? |
175
+ | **PFAS** | Where has PFOA or PFOS been detected above 4 ppt in Pennsylvania? |
176
+ | **What changed** | What new results were published in California this week? |
177
+ | **Market research** | Which Illinois systems serving over 50,000 people have detected PFAS? |
178
+
179
+ ### Scope it
180
+
181
+ ```python
182
+ answer = muriel.query("Any new detections?", pwsid="IL0316000", since="2026-01-01")
183
+ ```
184
+
185
+ ### Get exactly the fields you need
186
+
187
+ Pass a list of field names, a JSON schema or a Pydantic model, and the answer
188
+ comes back in that shape, ready for a database, a dashboard or another program:
189
+
190
+ ```python
191
+ latest = muriel.query(
192
+ "What was the most recent lead result for Chicago?",
193
+ pwsid="IL0316000",
194
+ schema=["sample_date", "result", "units", "above_action_level"],
195
+ )
196
+ ```
197
+
198
+ ```python
199
+ from typing import List
200
+ from pydantic import BaseModel
201
+ from muriel import Agent
202
+
203
+ class SystemSummary(BaseModel):
204
+ pwsid: str
205
+ system_name: str
206
+ total_samples: int
207
+ contaminants_found: List[str]
208
+
209
+ summary = Agent().query("Summarize water quality for IL0316000.", schema=SystemSummary)
210
+ print(summary.system_name, summary.total_samples)
211
+ ```
212
+
213
+ ### From question to CSV
214
+
215
+ Ask for an export and the file comes back with the answer:
216
+
217
+ ```python
218
+ answer = muriel.query("Which systems in Oregon have a lead 90th percentile above 10 ppb? "
219
+ "Export it to a CSV, largest systems first.")
220
+ for doc in answer.documents:
221
+ print(doc["filename"], doc["url"])
222
+ ```
223
+
224
+ ---
225
+
226
+ ## 3. Query the REST API directly
227
+
228
+ When you know exactly what you want, such as one system's full history or one
229
+ contaminant across the country, query the data directly. Same key as above.
230
+
231
+ ```python
232
+ from muriel import get_results, get_lead, get_systems
233
+
234
+ # One water system, history included
235
+ rows = get_results(pwsid="IL0316000", analyte="Lead", limit=100)
236
+
237
+ # A whole state, or one group of contaminants
238
+ rows = get_results(state="IL", analyte_group="pfas", limit=500)
239
+
240
+ # The water system that serves an address, then systems nearby
241
+ rows = get_results(address="121 N LaSalle St, Chicago, IL", radius_miles=10)
242
+
243
+ # The latest lead results nationwide
244
+ rows = get_lead(limit=50, detections_only=True)
245
+
246
+ # Every water system in a state, with size and latest sample
247
+ systems = get_systems(state="IL")
248
+ ```
249
+
250
+ The same calls work from any language over HTTP:
251
+
252
+ ```bash
253
+ curl "https://api.murielai.io/api/v1/records/system/IL0316000?result_analyte_name=Lead&limit=12" \
254
+ -H "X-API-Key: $MURIEL_API_KEY"
255
+ ```
256
+
257
+ Every record has the same fields whichever state it came from: the system, the
258
+ contaminant, the value as published and converted to mg/L, the sample date, the
259
+ place where known, and `era`: `"recent"` for the window states currently
260
+ publish, `"historical"` for the archive. Pass `era="all"` to include history in
261
+ the national feeds, or `era="recent"` to limit a system or state query to
262
+ current results.
263
+
264
+ | Function | What it returns |
265
+ |---|---|
266
+ | `get_results()` | Results by system, state, analyte group or address, with filters for dates, detections, concentration and analyte |
267
+ | `get_latest()`, `get_lead()`, `get_pfas()`, `get_coliform()`, `get_nitrate()`, `get_dbps()`, `get_arsenic()`, `get_manganese()`, `get_vocs()` | The latest results for one contaminant, nationwide |
268
+ | `get_systems()` | Water systems in a state |
269
+ | `get_state_sampling_points()`, `get_sampling_point_geocoding()` | Sampling points with a location |
270
+ | `get_state_facilities()`, `get_facility_geocoding()` | Facilities with a location, where states publish them |
271
+ | `SamplesClient`, `SystemsClient` | The same calls as objects |
272
+
273
+ Up to 1,000 records per call; page with `offset`. Monitoring requirements and
274
+ violation records (`get_requirements`, `get_violations`) are not available yet;
275
+ ask the agent about monitoring gaps and exceedances instead.
276
+
277
+ Interactive reference for every route and field:
278
+ [api.murielai.io/docs](https://api.murielai.io/docs).
279
+
280
+ ---
281
+
282
+ ## Pricing
283
+
284
+ One balance of credits on your Muriel account covers all three:
285
+
286
+ - **An agent question**, in the app or from code, is metered on the work it
287
+ takes: typically 1–3 credits, never more than 8.
288
+ - **Data calls**, through the connector's tools or the REST API, cost 1 credit
289
+ per 1,000 records returned, added up across calls. A call that returns
290
+ nothing is free.
291
+
292
+ Every account gets 8 free credits a month, usable on any of them. For more,
293
+ email [info@murielai.io](mailto:info@murielai.io).
294
+
295
+ ## Errors
296
+
297
+ ```python
298
+ from muriel import MurielAPIError, MurielCreditsError, get_results
299
+
300
+ try:
301
+ rows = get_results(pwsid="IL0316000")
302
+ except MurielCreditsError:
303
+ print("Out of credits.")
304
+ except MurielAPIError as e:
305
+ print(e.status_code, e.message)
306
+ ```
307
+
308
+ | Exception | When |
309
+ |---|---|
310
+ | `MurielAuthenticationError` | Missing or invalid API key (401) |
311
+ | `MurielValidationError` | A bad parameter (400) |
312
+ | `MurielCreditsError` | Out of credits (402); nothing was charged |
313
+ | `MurielNotFoundError` | Unknown system or resource (404) |
314
+ | `MurielRateLimitError` | Too many requests (429); wait and retry |
315
+ | `MurielServerError` | Not available (501), or a server error worth retrying (5xx) |
316
+
317
+ All of them inherit from `MurielAPIError`. Keys are only ever sent over HTTPS,
318
+ and never forwarded on a redirect.
319
+
320
+ ## Good to know
321
+
322
+ Muriel is a research and analysis tool, not a compliance determination.
323
+ Results are as states published them; coverage varies by state, and the
324
+ absence of a result is never evidence of compliance. Only your state primacy
325
+ agency can say whether a system is in compliance.
326
+
327
+ ## Documentation
328
+
329
+ - Guides and SDK reference: [docs.murielai.io](https://docs.murielai.io)
330
+ - MCP connector reference: [murielai.io/developers](https://murielai.io/developers#connector)
331
+ - REST API reference: [api.murielai.io/docs](https://api.murielai.io/docs)
332
+
333
+ ## Support
334
+
335
+ - Email: [info@murielai.io](mailto:info@murielai.io)
336
+ - Issues: [github.com/Muriel-AI-Inc/muriel-sdk-python/issues](https://github.com/Muriel-AI-Inc/muriel-sdk-python/issues)
337
+
338
+ ## License
339
+
340
+ MIT. Copyright (c) 2024-2026 Muriel AI, Inc. Use of Muriel is governed by the
341
+ terms at [murielai.io/terms](https://murielai.io/terms).
muriel-0.2.0/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # Muriel Python SDK
2
+
3
+ [Muriel](https://murielai.io) is the AI agent for U.S. drinking water data. It
4
+ answers plain-English questions about any public water system from the
5
+ monitoring results states publish, checks them against federal limits, and
6
+ shows the records behind every answer.
7
+
8
+ There are three ways to use it, and this README covers them in the order most
9
+ people need them:
10
+
11
+ 1. **[In your AI assistant](#1-use-muriel-in-your-ai-assistant-mcp-connector)**:
12
+ add the Muriel connector to Claude, ChatGPT, Cursor, VS Code or any MCP
13
+ client and ask in the chat you already use. No code, no key.
14
+ 2. **[The agent, from code](#2-ask-the-agent-from-code)**: `muriel.query("...")`
15
+ sends a question to the same agent and returns the answer, the records it
16
+ used, and any CSV it produced, shaped the way your program needs.
17
+ 3. **[The REST API](#3-query-the-rest-api-directly)**: exact, parameter-driven
18
+ queries for one system's history, one contaminant nationwide, every system
19
+ in a state, or the system serving an address. Use this package's functions or
20
+ call it over HTTP from any language.
21
+
22
+ All three draw on the same data and the same Muriel account and credits.
23
+
24
+ ## What Muriel covers
25
+
26
+ - **88 million+ results** from **32 states** and **63,000+ water systems**
27
+ - **Current:** many states update daily, often within days of sampling
28
+ - **Deep:** some states go back to the 1970s
29
+ - **Every contaminant states test for:** lead and copper, PFAS, nitrate,
30
+ disinfection byproducts, arsenic, coliform, VOCs and more
31
+
32
+ Every answer is built from the sample results themselves, not from summaries or
33
+ annual reports, and says how current each state's data is. A gap is never
34
+ passed off as a clean record. See exactly which states, contaminants and years
35
+ are covered, live, at [murielai.io/overview](https://murielai.io/overview).
36
+
37
+ Muriel is built for people who work with drinking water: utilities and the
38
+ regulators who oversee them, and the labs, engineering firms and vendors who
39
+ serve them.
40
+
41
+ ---
42
+
43
+ ## 1. Use Muriel in your AI assistant (MCP connector)
44
+
45
+ The quickest way to put Muriel to work. Add one URL to your assistant, sign in
46
+ to your Muriel account and click **Allow**. There is no API key to copy and
47
+ nothing to install.
48
+
49
+ **Connector URL:** `https://mcp.murielai.io/mcp`
50
+
51
+ | Assistant | How to add it |
52
+ |---|---|
53
+ | **Claude** (claude.ai and desktop) | Settings → Connectors → *Add custom connector* → name it Muriel, paste the URL → *Connect* |
54
+ | **Claude Code** | `claude mcp add --transport http --scope user muriel https://mcp.murielai.io/mcp`, then `/mcp` → muriel → *Authenticate* |
55
+ | **Cursor** | In `~/.cursor/mcp.json`: `{"mcpServers": {"muriel": {"url": "https://mcp.murielai.io/mcp"}}}` |
56
+ | **VS Code** (Copilot agent mode) | In `.vscode/mcp.json`: `{"servers": {"muriel": {"type": "http", "url": "https://mcp.murielai.io/mcp"}}}` |
57
+ | **ChatGPT** | Where custom connectors are enabled: Settings → Connectors → create one with the URL and OAuth |
58
+ | **Anything else** | Any client that supports remote MCP servers with OAuth takes the URL. For local-only (stdio) clients: `npx -y mcp-remote https://mcp.murielai.io/mcp` |
59
+
60
+ The first time your assistant uses Muriel it opens a Muriel page: sign in if
61
+ you need to, see what it is asking for, and click **Allow**. To disconnect,
62
+ use *Connected apps* on your [account page](https://murielai.io/account).
63
+
64
+ Then just ask, in the chat you already use:
65
+
66
+ > Anything new with Chicago's water this week?
67
+ >
68
+ > Which systems in Georgia had a nitrate result above the MCL in the last year?
69
+ >
70
+ > Where has PFOA or PFOS been detected above 4 ppt in Pennsylvania?
71
+
72
+ Your assistant calls Muriel whenever a question needs drinking water data and
73
+ answers from what Muriel returns. The connector gives it 16 read-only tools,
74
+ the same ones Muriel's own agent uses:
75
+
76
+ | Tools | What they answer |
77
+ |---|---|
78
+ | `find_water_system` | A utility or town name → its water system ID (PWSID) |
79
+ | `get_sample_results`, `summarize_sample_results`, `deep_history_search` | Individual results and summaries, current and archived |
80
+ | `get_notable_events`, `get_record_changes` | What changed in the latest updates |
81
+ | `analyte_trend` | Whether a contaminant is going up or down |
82
+ | `check_compliance_from_data`, `check_monitoring_gaps` | Running annual averages, 90th percentiles and missing samples |
83
+ | `rank_systems_by_size` | State-wide questions with no system named |
84
+ | `data_coverage`, `historical_depth` | What data exists for a state or system, and how far back |
85
+ | `lookup_standard`, `evaluate_result`, `resolve_analyte`, `search_regulations` | The federal standards and rules |
86
+
87
+ The connector can't see your Muriel conversations or documents, change your
88
+ account or buy anything. Full reference:
89
+ [murielai.io/developers](https://murielai.io/developers#connector).
90
+
91
+ ---
92
+
93
+ ## 2. Ask the agent from code
94
+
95
+ Install the package and set your key. Create a key on your
96
+ [account page](https://murielai.io/account).
97
+
98
+ ```bash
99
+ pip install muriel
100
+ export MURIEL_API_KEY="muriel_live_..."
101
+ ```
102
+
103
+ Every function reads `MURIEL_API_KEY`; you can also pass `api_key=` directly.
104
+
105
+ ```python
106
+ import muriel
107
+
108
+ answer = muriel.query("Is Chicago's water meeting federal limits for lead and copper?")
109
+
110
+ print(answer.summary) # the answer, in plain English
111
+ for r in answer.results[:5]: # the records it rests on
112
+ print(r["sample_collection_date"], r["system_name"],
113
+ r["result_analyte_name"], r["result_value"], r["result_unit"])
114
+ ```
115
+
116
+ `muriel.query()` asks the same agent that answers at
117
+ [murielai.io](https://murielai.io). It works out which systems, contaminants and
118
+ dates the question is about, pulls the results, compares them with MCLs, action
119
+ levels and treatment rules, and tells you when a state's data is out of date.
120
+ Each answer carries:
121
+
122
+ | Field | What it holds |
123
+ |---|---|
124
+ | `summary` | The answer in plain English |
125
+ | `results`, `count` | The records the answer rests on |
126
+ | `documents` | Any CSV or PDF the agent produced, with a download URL |
127
+ | `warning` | A caveat, when the data needs one |
128
+ | `execution_log` | The steps the agent took |
129
+ | `credits` | What the question used, and your balance after it |
130
+
131
+ ### What you can ask
132
+
133
+ | | |
134
+ |---|---|
135
+ | **Compliance** | Which systems in Georgia had a nitrate result above the MCL in the last year? |
136
+ | **Trends** | Which systems are within 20% of an MCL and trending up? |
137
+ | **PFAS** | Where has PFOA or PFOS been detected above 4 ppt in Pennsylvania? |
138
+ | **What changed** | What new results were published in California this week? |
139
+ | **Market research** | Which Illinois systems serving over 50,000 people have detected PFAS? |
140
+
141
+ ### Scope it
142
+
143
+ ```python
144
+ answer = muriel.query("Any new detections?", pwsid="IL0316000", since="2026-01-01")
145
+ ```
146
+
147
+ ### Get exactly the fields you need
148
+
149
+ Pass a list of field names, a JSON schema or a Pydantic model, and the answer
150
+ comes back in that shape, ready for a database, a dashboard or another program:
151
+
152
+ ```python
153
+ latest = muriel.query(
154
+ "What was the most recent lead result for Chicago?",
155
+ pwsid="IL0316000",
156
+ schema=["sample_date", "result", "units", "above_action_level"],
157
+ )
158
+ ```
159
+
160
+ ```python
161
+ from typing import List
162
+ from pydantic import BaseModel
163
+ from muriel import Agent
164
+
165
+ class SystemSummary(BaseModel):
166
+ pwsid: str
167
+ system_name: str
168
+ total_samples: int
169
+ contaminants_found: List[str]
170
+
171
+ summary = Agent().query("Summarize water quality for IL0316000.", schema=SystemSummary)
172
+ print(summary.system_name, summary.total_samples)
173
+ ```
174
+
175
+ ### From question to CSV
176
+
177
+ Ask for an export and the file comes back with the answer:
178
+
179
+ ```python
180
+ answer = muriel.query("Which systems in Oregon have a lead 90th percentile above 10 ppb? "
181
+ "Export it to a CSV, largest systems first.")
182
+ for doc in answer.documents:
183
+ print(doc["filename"], doc["url"])
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 3. Query the REST API directly
189
+
190
+ When you know exactly what you want, such as one system's full history or one
191
+ contaminant across the country, query the data directly. Same key as above.
192
+
193
+ ```python
194
+ from muriel import get_results, get_lead, get_systems
195
+
196
+ # One water system, history included
197
+ rows = get_results(pwsid="IL0316000", analyte="Lead", limit=100)
198
+
199
+ # A whole state, or one group of contaminants
200
+ rows = get_results(state="IL", analyte_group="pfas", limit=500)
201
+
202
+ # The water system that serves an address, then systems nearby
203
+ rows = get_results(address="121 N LaSalle St, Chicago, IL", radius_miles=10)
204
+
205
+ # The latest lead results nationwide
206
+ rows = get_lead(limit=50, detections_only=True)
207
+
208
+ # Every water system in a state, with size and latest sample
209
+ systems = get_systems(state="IL")
210
+ ```
211
+
212
+ The same calls work from any language over HTTP:
213
+
214
+ ```bash
215
+ curl "https://api.murielai.io/api/v1/records/system/IL0316000?result_analyte_name=Lead&limit=12" \
216
+ -H "X-API-Key: $MURIEL_API_KEY"
217
+ ```
218
+
219
+ Every record has the same fields whichever state it came from: the system, the
220
+ contaminant, the value as published and converted to mg/L, the sample date, the
221
+ place where known, and `era`: `"recent"` for the window states currently
222
+ publish, `"historical"` for the archive. Pass `era="all"` to include history in
223
+ the national feeds, or `era="recent"` to limit a system or state query to
224
+ current results.
225
+
226
+ | Function | What it returns |
227
+ |---|---|
228
+ | `get_results()` | Results by system, state, analyte group or address, with filters for dates, detections, concentration and analyte |
229
+ | `get_latest()`, `get_lead()`, `get_pfas()`, `get_coliform()`, `get_nitrate()`, `get_dbps()`, `get_arsenic()`, `get_manganese()`, `get_vocs()` | The latest results for one contaminant, nationwide |
230
+ | `get_systems()` | Water systems in a state |
231
+ | `get_state_sampling_points()`, `get_sampling_point_geocoding()` | Sampling points with a location |
232
+ | `get_state_facilities()`, `get_facility_geocoding()` | Facilities with a location, where states publish them |
233
+ | `SamplesClient`, `SystemsClient` | The same calls as objects |
234
+
235
+ Up to 1,000 records per call; page with `offset`. Monitoring requirements and
236
+ violation records (`get_requirements`, `get_violations`) are not available yet;
237
+ ask the agent about monitoring gaps and exceedances instead.
238
+
239
+ Interactive reference for every route and field:
240
+ [api.murielai.io/docs](https://api.murielai.io/docs).
241
+
242
+ ---
243
+
244
+ ## Pricing
245
+
246
+ One balance of credits on your Muriel account covers all three:
247
+
248
+ - **An agent question**, in the app or from code, is metered on the work it
249
+ takes: typically 1–3 credits, never more than 8.
250
+ - **Data calls**, through the connector's tools or the REST API, cost 1 credit
251
+ per 1,000 records returned, added up across calls. A call that returns
252
+ nothing is free.
253
+
254
+ Every account gets 8 free credits a month, usable on any of them. For more,
255
+ email [info@murielai.io](mailto:info@murielai.io).
256
+
257
+ ## Errors
258
+
259
+ ```python
260
+ from muriel import MurielAPIError, MurielCreditsError, get_results
261
+
262
+ try:
263
+ rows = get_results(pwsid="IL0316000")
264
+ except MurielCreditsError:
265
+ print("Out of credits.")
266
+ except MurielAPIError as e:
267
+ print(e.status_code, e.message)
268
+ ```
269
+
270
+ | Exception | When |
271
+ |---|---|
272
+ | `MurielAuthenticationError` | Missing or invalid API key (401) |
273
+ | `MurielValidationError` | A bad parameter (400) |
274
+ | `MurielCreditsError` | Out of credits (402); nothing was charged |
275
+ | `MurielNotFoundError` | Unknown system or resource (404) |
276
+ | `MurielRateLimitError` | Too many requests (429); wait and retry |
277
+ | `MurielServerError` | Not available (501), or a server error worth retrying (5xx) |
278
+
279
+ All of them inherit from `MurielAPIError`. Keys are only ever sent over HTTPS,
280
+ and never forwarded on a redirect.
281
+
282
+ ## Good to know
283
+
284
+ Muriel is a research and analysis tool, not a compliance determination.
285
+ Results are as states published them; coverage varies by state, and the
286
+ absence of a result is never evidence of compliance. Only your state primacy
287
+ agency can say whether a system is in compliance.
288
+
289
+ ## Documentation
290
+
291
+ - Guides and SDK reference: [docs.murielai.io](https://docs.murielai.io)
292
+ - MCP connector reference: [murielai.io/developers](https://murielai.io/developers#connector)
293
+ - REST API reference: [api.murielai.io/docs](https://api.murielai.io/docs)
294
+
295
+ ## Support
296
+
297
+ - Email: [info@murielai.io](mailto:info@murielai.io)
298
+ - Issues: [github.com/Muriel-AI-Inc/muriel-sdk-python/issues](https://github.com/Muriel-AI-Inc/muriel-sdk-python/issues)
299
+
300
+ ## License
301
+
302
+ MIT. Copyright (c) 2024-2026 Muriel AI, Inc. Use of Muriel is governed by the
303
+ terms at [murielai.io/terms](https://murielai.io/terms).