# Get a single annex
Source: https://docs.law4devs.eu/api-reference/annexes/get-a-single-annex
/openapi.yaml get /frameworks/{slug}/annexes/{annex_number}
# List annexes for a framework
Source: https://docs.law4devs.eu/api-reference/annexes/list-annexes-for-a-framework
/openapi.yaml get /frameworks/{slug}/annexes
# Get a single article
Source: https://docs.law4devs.eu/api-reference/articles/get-a-single-article
/openapi.yaml get /frameworks/{slug}/articles/{article_number}
Returns the full article including its content, ordered paragraphs,
and associated tags.
# Get related articles and requirements
Source: https://docs.law4devs.eu/api-reference/articles/get-related-articles-and-requirements
/openapi.yaml get /frameworks/{slug}/articles/{article_number}/related
Returns articles and requirements that cross-reference this article
via `linked_article_numbers`. Includes both outbound references
(articles this one links to) and inbound references (articles linking
to this one).
# List articles for a framework
Source: https://docs.law4devs.eu/api-reference/articles/list-articles-for-a-framework
/openapi.yaml get /frameworks/{slug}/articles
Returns paginated articles for a framework. Supports optional filtering
by tag slug and full-text search within article titles and content.
# Content update history for a framework
Source: https://docs.law4devs.eu/api-reference/changelog/content-update-history-for-a-framework
/openapi.yaml get /frameworks/{slug}/changelog
Returns content update log entries for a specific framework, newest first.
Use this to check when data was last updated and how many records
were added or modified.
# Global content update changelog
Source: https://docs.law4devs.eu/api-reference/changelog/global-content-update-changelog
/openapi.yaml get /changelog
Returns content update log entries across all frameworks, newest first.
Filter by framework slug or date to scope results.
# Compare articles across multiple frameworks
Source: https://docs.law4devs.eu/api-reference/compare/compare-articles-across-multiple-frameworks
/openapi.yaml get /compare
Returns articles grouped by framework slug for side-by-side comparison.
Accepts a comma-separated list of framework slugs. Optionally filter
articles by tag to focus the comparison on a specific topic.
# List all compliance deadlines across all frameworks
Source: https://docs.law4devs.eu/api-reference/compliance/list-all-compliance-deadlines-across-all-frameworks
/openapi.yaml get /compliance/deadlines
Returns all compliance deadlines across all active frameworks, ordered
by date. Useful for building compliance calendars. Filter by framework
slug to scope to a single regulation.
# List compliance deadlines for a framework
Source: https://docs.law4devs.eu/api-reference/compliance/list-compliance-deadlines-for-a-framework
/openapi.yaml get /frameworks/{slug}/compliance/deadlines
Returns all compliance deadlines for the framework, ordered by date.
# Get framework detail
Source: https://docs.law4devs.eu/api-reference/frameworks/get-framework-detail
/openapi.yaml get /frameworks/{slug}
Returns full framework metadata including counts (articles, recitals,
requirements, annexes, tags) and coverage percentages.
# List all active frameworks
Source: https://docs.law4devs.eu/api-reference/frameworks/list-all-active-frameworks
/openapi.yaml get /frameworks
Returns all active EU regulatory frameworks ordered by slug.
# Health check
Source: https://docs.law4devs.eu/api-reference/health/health-check
/openapi.yaml get /health
Returns API status and database connectivity.
# Get a single recital
Source: https://docs.law4devs.eu/api-reference/recitals/get-a-single-recital
/openapi.yaml get /frameworks/{slug}/recitals/{recital_number}
# List recitals for a framework
Source: https://docs.law4devs.eu/api-reference/recitals/list-recitals-for-a-framework
/openapi.yaml get /frameworks/{slug}/recitals
Returns paginated recitals (preamble paragraphs) ordered by recital number.
# List requirements across all frameworks
Source: https://docs.law4devs.eu/api-reference/requirements/list-requirements-across-all-frameworks
/openapi.yaml get /requirements
Cross-framework requirements endpoint. Returns all requirements from
all active frameworks. Optionally filter to a single framework,
stakeholder role, type, or tag.
# List requirements for a framework
Source: https://docs.law4devs.eu/api-reference/requirements/list-requirements-for-a-framework
/openapi.yaml get /frameworks/{slug}/requirements
Returns paginated compliance requirements extracted from a framework's
articles. Filterable by stakeholder role, requirement type, and tag.
# Full-text search across articles, recitals, and requirements
Source: https://docs.law4devs.eu/api-reference/search/full-text-search-across-articles-recitals-and-requirements
/openapi.yaml get /search
Searches article titles and content, recital content, and requirement
text across all active frameworks. Returns ranked results with context
snippets. Supports filtering by framework, tag, stakeholder role,
and result type.
Query must be at least 2 characters and at most 500 characters.
# Coverage statistics for a single framework
Source: https://docs.law4devs.eu/api-reference/stats/coverage-statistics-for-a-single-framework
/openapi.yaml get /frameworks/{slug}/stats
Returns article, recital, requirement, annex, and tag counts for the
framework, including coverage percentages vs. expected totals.
# Coverage statistics for all active frameworks
Source: https://docs.law4devs.eu/api-reference/stats/coverage-statistics-for-all-active-frameworks
/openapi.yaml get /stats
Returns stats for every active framework in one call.
# Get tag detail
Source: https://docs.law4devs.eu/api-reference/tags/get-tag-detail
/openapi.yaml get /tags/{slug}
Returns a single tag with its keywords and metadata.
# List all tags
Source: https://docs.law4devs.eu/api-reference/tags/list-all-tags
/openapi.yaml get /tags
Returns all semantic tags used to classify articles and requirements.
# Changelog
Source: https://docs.law4devs.eu/changelog
API and SDK release history.
## v1.0.2 — March 2026
* **Java SDK** `1.0.2` — `Requirement` and `ComplianceDeadline` models corrected to match actual API response fields
* **Rust SDK** `1.0.2` — `ComplianceDeadline` model corrected; added deserializer to handle `article_number` returned as integer
* **Flutter SDK** `1.0.2` — `ComplianceDeadline` and `Requirement` models corrected to match actual API response fields
## v1.0.0 — March 2026
* **PHP SDK** (`law4devs/law4devs`) published to Packagist — PHP 8.1+, Composer, zero extra dependencies
* **Java SDK** (`eu.law4devs:law4devs-sdk`) published to Maven Central — Java 17+, zero dependencies
* **Flutter SDK** (`law4devs`) published to pub.dev — Dart 3+, Flutter & CLI, Stream-based auto-pagination
* **Rust SDK** (`law4devs`) published to crates.io — Rust 1.85+, async Tokio runtime
* **ReDoc** API reference now available at `https://api.law4devs.eu/redoc` — clean read-only view ideal for sharing with stakeholders
## v1.0.0 — 2026
* **19 EU frameworks** available (CRA, GDPR, NIS2, DORA, AI Act, eIDAS 2.0, DSA, DMA, Data Act, DGA, MiCAR, and more)
* **21 REST API endpoints** covering frameworks, articles, recitals, annexes, requirements, compliance deadlines, tags, search, stats, compare, and changelog
* **Python SDK** (`law4devs`) published to PyPI
* **TypeScript SDK** (`law4devs`) published to npm
* OpenAPI 3.0.3 spec available at `/v1/openapi.json`
* Interactive Swagger UI at `https://api.law4devs.eu/docs`
* Rate limiting: 100 requests/minute per IP
* CORS enabled for all origins on GET/OPTIONS
# OpenAPI Spec
Source: https://docs.law4devs.eu/developer-tools/openapi-spec
Machine-readable OpenAPI 3.0.3 specification for import and code generation
The Law4Devs API is fully described in an OpenAPI 3.0.3 specification. You can use
this to import the API into your preferred HTTP client, generate typed SDK code, or
run automated contract tests.
## Access the Spec
| Format | URL |
| ------ | ----------------------------------------- |
| JSON | `https://api.law4devs.eu/v1/openapi.json` |
```bash theme={null}
# Download the JSON spec
curl https://api.law4devs.eu/v1/openapi.json -o law4devs-openapi.json
```
## Import into API Clients
1. Open Postman and click **Import**
2. Select **Link** and paste `https://api.law4devs.eu/v1/openapi.json`
3. Click **Import** — all endpoints appear as a new collection
4. Set the `X-API-Key` variable in the collection variables to your API key
1. Click **Create** → **Import from URL**
2. Paste `https://api.law4devs.eu/v1/openapi.json`
3. Click **Fetch and Import**
4. Add `X-API-Key` as a header in the collection settings
1. In your collection, click **...** → **Import**
2. Choose **OpenAPI** and paste the spec URL or upload the downloaded file
3. Bruno generates requests for all endpoints automatically
## Code Generation
Use [openapi-generator](https://openapi-generator.tech/) to generate a typed client
in any language from the spec:
```bash theme={null}
# Generate a Python client
openapi-generator generate \
-i https://api.law4devs.eu/v1/openapi.json \
-g python \
-o ./generated-client
# Generate a Go client
openapi-generator generate \
-i https://api.law4devs.eu/v1/openapi.json \
-g go \
-o ./generated-client
```
The official SDKs (Python, TypeScript, PHP) are hand-crafted and include features
like auto-pagination and retry logic that generated clients do not provide. Use
code generation only if your language isn't covered by an official SDK.
## Spec Details
| Property | Value |
| --------------- | --------------------------------------- |
| Spec version | OpenAPI 3.0.3 |
| API version | 1.0 |
| Total endpoints | 21 |
| Authentication | `X-API-Key` header |
| Response format | JSON |
| Pagination | Cursor-based with `page` and `per_page` |
## Versioning
The current API version is `1.0`. Every response includes the `X-API-Version` header
so your application can detect version changes programmatically. Breaking changes will
increment the major version and be announced in the [Changelog](/changelog).
# Developer Tools
Source: https://docs.law4devs.eu/developer-tools/overview
Interactive API explorer, spec viewer, and machine-readable OpenAPI documentation
Law4Devs ships with a complete set of developer tools — from an interactive API explorer
you can use to try any endpoint right in your browser, to a machine-readable OpenAPI spec
you can import into Postman, Insomnia, or a code generator.
Interactive API explorer with live request testing and authentication support.
Clean, read-only API reference — ideal for sharing with stakeholders.
Machine-readable OpenAPI 3.0.3 spec for Postman, Insomnia, and code generation.
## Quick Links
| Tool | URL | Use For |
| ------------ | ----------------------------------------- | ------------------- |
| Swagger UI | `https://api.law4devs.eu/docs` | Interactive testing |
| ReDoc | `https://api.law4devs.eu/redoc` | Reading & sharing |
| OpenAPI JSON | `https://api.law4devs.eu/v1/openapi.json` | Import into tools |
# ReDoc
Source: https://docs.law4devs.eu/developer-tools/redoc
Clean, read-only API reference documentation
ReDoc renders the same OpenAPI spec as Swagger UI, but as a clean three-panel
reference document — ideal for reading carefully, printing, or sharing with
non-technical stakeholders.
**URL:** `https://api.law4devs.eu/redoc`
## Swagger UI vs ReDoc
| | Swagger UI | ReDoc |
| ------------------------------- | :---------------: | :---------------: |
| Interactive "Try it out" | ✓ | — |
| Read request/response schemas | ✓ | ✓ |
| Clean print / PDF export | — | ✓ |
| Deep-link to specific endpoints | — | ✓ |
| Best for | Engineers testing | Reviews & sharing |
## Navigating ReDoc
ReDoc organises endpoints in a left sidebar grouped by tag. Click any endpoint
to jump directly to its documentation. The right column always shows example
request and response payloads.
**Direct link to an endpoint:** append the anchor to the URL, for example:
```
https://api.law4devs.eu/redoc#tag/Frameworks/operation/list_frameworks
```
## Sharing with Stakeholders
ReDoc is the recommended view to share with compliance officers, legal teams,
or product managers who need to understand the API without running code.
The layout is clean and printable — use your browser's Print → Save as PDF
for a permanent offline copy.
# Swagger UI
Source: https://docs.law4devs.eu/developer-tools/swagger
Interactively explore and test all Law4Devs API endpoints in your browser
Swagger UI provides a browser-based interface where you can browse every endpoint,
read its documentation, and make live API calls — all without writing any code.
**URL:** `https://api.law4devs.eu/docs`
## Authenticating in Swagger UI
Before you can use "Try it out", you need to provide your API key:
Navigate to `https://api.law4devs.eu/docs` in your browser.
Click the **Authorize** button (lock icon) in the top-right of the page.
In the dialog that appears, paste your API key into the `X-API-Key` field and
click **Authorize**.
```
X-API-Key: YOUR_API_KEY
```
Close the dialog. Expand any endpoint, click **Try it out**, fill in any
parameters, and hit **Execute**.
## Example: Listing Frameworks
1. Expand `GET /v1/frameworks`
2. Click **Try it out**
3. Set `per_page` to `5`
4. Click **Execute**
You'll see the full request URL, the `curl` command you can copy, and the live
JSON response directly below.
## When to Use Swagger UI
Try endpoints before writing SDK code — verify responses and parameters quickly.
Reproduce API issues interactively to isolate whether the problem is client-side.
Discover all available endpoints and their parameters in one place.
Share a specific endpoint with a colleague by copying the URL from the address bar.
# Supported Frameworks
Source: https://docs.law4devs.eu/frameworks
All 19 EU regulatory frameworks available in the Law4Devs API.
Use these slugs in API calls and SDK methods: `client.articles.list("gdpr")`, `/frameworks/cra/articles`, etc.
## Framework List
| Slug | Name | CELEX | Status | Documentation |
| ---------------------------------------------------- | -------------------------------------------- | ---------- | ---------- | ------------------------------------------ |
| [`cra`](/frameworks/cra) | Cyber Resilience Act | 32024R2847 | Active | [View Docs](/frameworks/cra) |
| [`nis2`](/frameworks/nis2) | NIS2 Directive | 32022L2555 | Active | [View Docs](/frameworks/nis2) |
| [`dora`](/frameworks/dora) | Digital Operational Resilience Act | 32022R2554 | Active | [View Docs](/frameworks/dora) |
| [`gdpr`](/frameworks/gdpr) | General Data Protection Regulation | 32016R0679 | Active | [View Docs](/frameworks/gdpr) |
| [`ai_act`](/frameworks/ai_act) | AI Act | 32024R1689 | Active | [View Docs](/frameworks/ai_act) |
| [`eidas`](/frameworks/eidas) | eIDAS Regulation | 32014R0910 | Active | [View Docs](/frameworks/eidas) |
| [`dsa`](/frameworks/dsa) | Digital Services Act | 32022R2065 | Active | [View Docs](/frameworks/dsa) |
| [`dma`](/frameworks/dma) | Digital Markets Act | 32022R1925 | Active | [View Docs](/frameworks/dma) |
| [`data_act`](/frameworks/data_act) | Data Act | 32023R2854 | Active | [View Docs](/frameworks/data_act) |
| [`dga`](/frameworks/dga) | Data Governance Act | 32022R0868 | Active | [View Docs](/frameworks/dga) |
| [`eidas2`](/frameworks/eidas2) | eIDAS 2.0 | 32024R1183 | Active | [View Docs](/frameworks/eidas2) |
| [`cer`](/frameworks/cer) | Critical Entities Resilience Directive | 32022L2557 | Active | [View Docs](/frameworks/cer) |
| [`psd2`](/frameworks/psd2) | Payment Services Directive 2 | 32015L2366 | Active | [View Docs](/frameworks/psd2) |
| [`mica`](/frameworks/mica) | Markets in Crypto-Assets Regulation | 32023R1114 | Active | [View Docs](/frameworks/mica) |
| [`cybersecurity_act`](/frameworks/cybersecurity_act) | Cybersecurity Act | 32019R0881 | Active | [View Docs](/frameworks/cybersecurity_act) |
| [`eprivacy`](/frameworks/eprivacy) | ePrivacy Directive | 32002L0058 | Active | [View Docs](/frameworks/eprivacy) |
| [`red`](/frameworks/red) | Radio Equipment Directive | 32014L0053 | Active | [View Docs](/frameworks/red) |
| [`csrd`](/frameworks/csrd) | Corporate Sustainability Reporting Directive | 32022L2464 | Active | [View Docs](/frameworks/csrd) |
| [`nis1`](/frameworks/nis1) | NIS Directive (Original) | 32016L1148 | Superseded | [View Docs](/frameworks/nis1) |
`nis1` has `status: "superseded"` — it is still accessible for historical reference but NIS2 is the current directive.
## Stakeholder Roles
Across all frameworks, the following stakeholder roles are defined:
| Role | Description |
| --------------------------- | ------------------------------------------------------------------------ |
| `manufacturer` | Entities that develop, produce, or place products/services on the market |
| `importer` | Entities that bring products/services from third countries into the EU |
| `distributor` | Entities that make products/services available in the EU supply chain |
| `authorized_representative` | EU-based representatives for non-EU entities |
| `open_source_steward` | Maintainers of open source components (CRA-specific) |
| `notified_body` | Designated conformity assessment bodies |
| `market_surveillance` | National competent authorities overseeing compliance |
| `consumer` | End users, data subjects, or recipients of services |
## Requirement Types
Requirements across frameworks are classified into these types:
| Type | Description |
| ------------- | ---------------------------------------------------------------- |
| `obligation` | Mandatory actions that must be taken |
| `prohibition` | Actions or practices that are forbidden |
| `right` | Entitlements granted to stakeholders (e.g., data subject rights) |
| `procedure` | Processes that must be followed (e.g., incident reporting) |
| `general` | General provisions, definitions, and scope |
## Using Slugs
```bash theme={null}
# Get all CRA articles
curl https://api.law4devs.eu/v1/frameworks/cra/articles
# Get GDPR framework details
curl https://api.law4devs.eu/v1/frameworks/gdpr
# Get AI Act requirements by stakeholder role
curl https://api.law4devs.eu/v1/frameworks/ai_act/requirements?role=manufacturer
# Compare NIS2 and DORA
curl "https://api.law4devs.eu/v1/compare?frameworks=nis2,dora"
```
```python theme={null}
# Python SDK example
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List articles from multiple frameworks
cra_articles = client.articles.list("cra")
gdpr_articles = client.articles.list("gdpr")
# Get requirements by stakeholder role
requirements = client.requirements.list("ai_act", role="manufacturer")
# Search across frameworks
results = client.search(query="incident reporting", frameworks=["nis2", "dora", "cra"])
```
# AI Act
Source: https://docs.law4devs.eu/frameworks/ai_act
Regulation (EU) 2024/1689 laying down harmonised rules on artificial intelligence.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32024R1689 |
| **Status** | Active |
| **Articles** | 113 |
| **Recitals** | 180 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32024R1689) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | ------------------------------------ |
| 2024-08-01 | Entry into force | Regulation enters into force |
| 2025-02-02 | Partial application | Prohibited AI systems rules apply |
| 2027-08-02 | Full application | Full application of all requirements |
## Stakeholder Roles
The AI Act defines obligations for the following stakeholder roles:
* **manufacturer** - Providers of AI systems (developers, vendors)
* **importer** - Entities placing AI systems from third countries on EU market
* **distributor** - Entities making AI systems available in the EU
* **authorized\_representative** - EU representatives for non-EU providers
* **notified\_body** - Conformity assessment bodies for high-risk AI
* **market\_surveillance** - National competent authorities
* **consumer** - Users and persons affected by AI systems
## Requirement Types
Requirements in the AI Act are classified as:
* **obligation** - Mandatory requirements for AI system providers
* **prohibition** - Prohibited AI practices (unacceptable risk)
* **right** - Rights of persons affected by AI systems
* **procedure** - Conformity assessment and post-market monitoring procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all AI Act articles
curl https://api.law4devs.eu/v1/frameworks/ai_act/articles
# Get AI Act framework details
curl https://api.law4devs.eu/v1/frameworks/ai_act
# Get AI Act requirements
curl https://api.law4devs.eu/v1/frameworks/ai_act/requirements
# Get AI Act recitals
curl https://api.law4devs.eu/v1/frameworks/ai_act/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all AI Act articles
articles = client.articles.list("ai_act")
# Get AI Act requirements by stakeholder role
requirements = client.requirements.list("ai_act", role="manufacturer")
# Search AI Act articles by tag
articles = client.articles.search("ai_act", query="high-risk")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all AI Act articles
const articles = await client.articles.list('ai_act');
// Get AI Act requirements by stakeholder role
const requirements = await client.requirements.list('ai_act', { role: 'manufacturer' });
```
## Key Topics
* Prohibited AI practices (unacceptable risk)
* High-risk AI system classification and requirements
* General-purpose AI models and GPAI obligations
* Transparency requirements for certain AI systems
* Conformity assessment and CE marking
* Post-market monitoring and incident reporting
* AI regulatory sandboxes and testing
# Critical Entities Resilience Directive (CER)
Source: https://docs.law4devs.eu/frameworks/cer
Directive (EU) 2022/2557 on the Resilience of Critical Entities.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32022L2557 |
| **Status** | Active |
| **Articles** | 29 |
| **Recitals** | 45 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022L2557) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------ |
| 2022-12-27 | Entry into force | Directive enters into force |
| 2024-10-17 | Transposition | Member States transposition deadline |
## Stakeholder Roles
The CER Directive defines obligations for the following stakeholder roles:
* **manufacturer** - Providers of security solutions for critical entities
* **importer** - Entities bringing security services into the EU
* **distributor** - Service providers supporting critical infrastructure
* **authorized\_representative** - EU representatives for non-EU providers
* **notified\_body** - Bodies for resilience assessment
* **market\_surveillance** - National competent authorities
* **consumer** - Recipients of essential services
## Requirement Types
Requirements in CER are classified as:
* **obligation** - Mandatory resilience and risk assessment requirements
* **prohibition** - Restricted practices affecting critical infrastructure
* **right** - Rights of critical entities and service recipients
* **procedure** - Incident notification and reporting procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all CER articles
curl https://api.law4devs.eu/v1/frameworks/cer/articles
# Get CER framework details
curl https://api.law4devs.eu/v1/frameworks/cer
# Get CER requirements
curl https://api.law4devs.eu/v1/frameworks/cer/requirements
# Get CER recitals
curl https://api.law4devs.eu/v1/frameworks/cer/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all CER articles
articles = client.articles.list("cer")
# Get CER requirements by stakeholder role
requirements = client.requirements.list("cer", role="manufacturer")
# Search CER articles by tag
articles = client.articles.search("cer", query="critical infrastructure")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all CER articles
const articles = await client.articles.list('cer');
// Get CER requirements by stakeholder role
const requirements = await client.requirements.list('cer', { role: 'manufacturer' });
```
## Key Topics
* Critical entity identification and designation
* Risk assessment and resilience measures
* Physical and digital security requirements
* Incident notification obligations
* Cross-border cooperation and information exchange
* Supervision and enforcement mechanisms
* Relationship with NIS2 Directive (sister legislation)
# Cyber Resilience Act (CRA)
Source: https://docs.law4devs.eu/frameworks/cra
Regulation (EU) 2024/2847 on horizontal cybersecurity requirements for products with digital elements.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32024R2847 |
| **Status** | Active |
| **Articles** | 71 |
| **Recitals** | 130 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32024R2847) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | ------------------------------------------ |
| 2024-12-10 | Entry into force | Regulation enters into force |
| 2026-06-11 | Partial application | Chapter IV applies (Reporting obligations) |
| 2026-09-11 | Partial application | Article 14 applies (Delegated acts) |
| 2027-12-11 | Full application | Full application of all requirements |
## Stakeholder Roles
The CRA defines obligations for the following stakeholder roles:
* **manufacturer** - Places products with digital elements on the EU market
* **importer** - Imports products from third countries into the EU
* **distributor** - Makes products available in the EU supply chain
* **authorized\_representative** - Acts on behalf of manufacturers
* **open\_source\_steward** - Manages open source components
* **notified\_body** - Conformity assessment bodies
* **market\_surveillance** - National authorities overseeing compliance
* **consumer** - End users of products with digital elements
## Requirement Types
Requirements in the CRA are classified as:
* **obligation** - Mandatory actions that must be taken
* **prohibition** - Actions that are forbidden
* **right** - Entitlements granted to stakeholders
* **procedure** - Processes that must be followed
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all CRA articles
curl https://api.law4devs.eu/v1/frameworks/cra/articles
# Get CRA framework details
curl https://api.law4devs.eu/v1/frameworks/cra
# Get CRA requirements
curl https://api.law4devs.eu/v1/frameworks/cra/requirements
# Get CRA recitals
curl https://api.law4devs.eu/v1/frameworks/cra/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all CRA articles
articles = client.articles.list("cra")
# Get CRA requirements by stakeholder role
requirements = client.requirements.list("cra", role="manufacturer")
# Search CRA articles by tag
articles = client.articles.search("cra", query="vulnerability")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all CRA articles
const articles = await client.articles.list('cra');
// Get CRA requirements by stakeholder role
const requirements = await client.requirements.list('cra', { role: 'manufacturer' });
```
## Key Topics
* Essential requirements for products with digital elements
* Vulnerability handling and disclosure
* CE marking and conformity assessment
* Market surveillance and enforcement
* Supply chain security obligations
# Corporate Sustainability Reporting Directive (CSRD)
Source: https://docs.law4devs.eu/frameworks/csrd
Directive (EU) 2022/2464 on Corporate Sustainability Reporting.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32022L2464 |
| **Status** | Active |
| **Articles** | 8 |
| **Recitals** | 84 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022L2464) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | ------------------------------------------------------- |
| 2023-01-05 | Entry into force | Directive enters into force |
| 2025-01-01 | Partial application | Large public-interest entities (FY2024 reporting) apply |
| 2026-01-01 | Partial application | Large companies (FY2025 reporting) apply |
| 2027-01-01 | Partial application | Listed SMEs (FY2026 reporting) apply |
## Stakeholder Roles
The CSRD defines obligations for the following stakeholder roles:
* **manufacturer** - Developers of sustainability reporting software and tools
* **importer** - Non-EU companies with significant EU operations
* **distributor** - Assurance service providers for sustainability data
* **authorized\_representative** - EU representatives for non-EU undertakings
* **notified\_body** - Independent assurance service providers
* **market\_surveillance** - National competent authorities
* **consumer** - Investors and stakeholders using sustainability information
## Requirement Types
Requirements in CSRD are classified as:
* **obligation** - Mandatory sustainability reporting requirements
* **prohibition** - Restricted reporting practices and greenwashing
* **right** - Rights of stakeholders to access sustainability information
* **procedure** - Assurance and digital tagging procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all CSRD articles
curl https://api.law4devs.eu/v1/frameworks/csrd/articles
# Get CSRD framework details
curl https://api.law4devs.eu/v1/frameworks/csrd
# Get CSRD requirements
curl https://api.law4devs.eu/v1/frameworks/csrd/requirements
# Get CSRD recitals
curl https://api.law4devs.eu/v1/frameworks/csrd/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all CSRD articles
articles = client.articles.list("csrd")
# Get CSRD requirements by stakeholder role
requirements = client.requirements.list("csrd", role="manufacturer")
# Search CSRD articles by tag
articles = client.articles.search("csrd", query="ESG")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all CSRD articles
const articles = await client.articles.list('csrd');
// Get CSRD requirements by stakeholder role
const requirements = await client.requirements.list('csrd', { role: 'manufacturer' });
```
## Key Topics
* Sustainability reporting scope and applicability
* European Sustainability Reporting Standards (ESRS)
* Double materiality assessment (impact and financial)
* Environmental, Social, and Governance (ESG) disclosures
* Digital tagging and machine-readable format (XHTML)
* Limited assurance requirements
* Value chain reporting (upstream and downstream)
* Non-EU company reporting obligations
# Cybersecurity Act (CSA)
Source: https://docs.law4devs.eu/frameworks/cybersecurity_act
Regulation (EU) 2019/881 on ENISA and on ICT Cybersecurity Certification.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32019R0881 |
| **Status** | Active |
| **Articles** | 69 |
| **Recitals** | 110 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32019R0881) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------------------- |
| 2019-06-27 | Entry into force | Regulation enters into force and full application |
## Stakeholder Roles
The Cybersecurity Act defines obligations for the following stakeholder roles:
* **manufacturer** - ICT product and service providers seeking certification
* **importer** - Entities bringing certified ICT products into the EU
* **distributor** - Providers of certified cybersecurity services
* **authorized\_representative** - EU representatives for non-EU manufacturers
* **notified\_body** - Conformity assessment bodies for cybersecurity certification
* **market\_surveillance** - National certification authorities and ENISA
* **consumer** - Users of certified ICT products and services
## Requirement Types
Requirements in the Cybersecurity Act are classified as:
* **obligation** - Mandatory requirements for certification schemes
* **prohibition** - Restricted certification claims and practices
* **right** - Rights of certificate holders and users
* **procedure** - Certification and peer review procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all Cybersecurity Act articles
curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act/articles
# Get Cybersecurity Act framework details
curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act
# Get Cybersecurity Act requirements
curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act/requirements
# Get Cybersecurity Act recitals
curl https://api.law4devs.eu/v1/frameworks/cybersecurity_act/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all Cybersecurity Act articles
articles = client.articles.list("cybersecurity_act")
# Get Cybersecurity Act requirements by stakeholder role
requirements = client.requirements.list("cybersecurity_act", role="manufacturer")
# Search Cybersecurity Act articles by tag
articles = client.articles.search("cybersecurity_act", query="certification")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all Cybersecurity Act articles
const articles = await client.articles.list('cybersecurity_act');
// Get Cybersecurity Act requirements by stakeholder role
const requirements = await client.requirements.list('cybersecurity_act', { role: 'manufacturer' });
```
## Key Topics
* ENISA (European Union Agency for Cybersecurity) mandate and governance
* European cybersecurity certification schemes
* ICT product, service, and process certification
* Assurance levels (basic, substantial, high)
* Mutual recognition of certificates across Member States
* Peer review mechanisms for certification bodies
* Relationship with sector-specific regulations
# Data Act
Source: https://docs.law4devs.eu/frameworks/data_act
Regulation (EU) 2023/2854 on Harmonised Rules on Fair Access to and Use of Data.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32023R2854 |
| **Status** | Active |
| **Articles** | 50 |
| **Recitals** | 120 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32023R2854) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------ |
| 2024-01-11 | Entry into force | Regulation enters into force |
| 2025-09-12 | Full application | Full application of all requirements |
## Stakeholder Roles
The Data Act defines obligations for the following stakeholder roles:
* **manufacturer** - Manufacturers of connected products and IoT devices
* **importer** - Entities placing connected products on the EU market
* **distributor** - Providers of related services
* **authorized\_representative** - EU representatives for non-EU manufacturers
* **notified\_body** - Bodies for conformity assessment
* **market\_surveillance** - National competent authorities
* **consumer** - Data holders and users of connected products
## Requirement Types
Requirements in the Data Act are classified as:
* **obligation** - Mandatory data access and sharing requirements
* **prohibition** - Restrictions on data use and contractual terms
* **right** - Rights of data holders (users) to access and share data
* **procedure** - Data sharing request and compensation procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all Data Act articles
curl https://api.law4devs.eu/v1/frameworks/data_act/articles
# Get Data Act framework details
curl https://api.law4devs.eu/v1/frameworks/data_act
# Get Data Act requirements
curl https://api.law4devs.eu/v1/frameworks/data_act/requirements
# Get Data Act recitals
curl https://api.law4devs.eu/v1/frameworks/data_act/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all Data Act articles
articles = client.articles.list("data_act")
# Get Data Act requirements by stakeholder role
requirements = client.requirements.list("data_act", role="manufacturer")
# Search Data Act articles by tag
articles = client.articles.search("data_act", query="data sharing")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all Data Act articles
const articles = await client.articles.list('data_act');
// Get Data Act requirements by stakeholder role
const requirements = await client.requirements.list('data_act', { role: 'manufacturer' });
```
## Key Topics
* User rights to access and use IoT-generated data
* Data holder and data recipient obligations
* Fair, reasonable, and non-discriminatory (FRAND) terms
* Switching between data processing services
* Interoperability requirements for connected products
* Public sector data access in exceptional circumstances
* Protection of trade secrets and confidential information
* Unfair contractual terms protection for SMEs
# Data Governance Act (DGA)
Source: https://docs.law4devs.eu/frameworks/dga
Regulation (EU) 2022/868 on European Data Governance.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32022R0868 |
| **Status** | Active |
| **Articles** | 38 |
| **Recitals** | 63 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R0868) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------ |
| 2022-06-23 | Entry into force | Regulation enters into force |
| 2023-09-24 | Full application | Full application of all requirements |
## Stakeholder Roles
The DGA defines obligations for the following stakeholder roles:
* **manufacturer** - Developers of data intermediation services
* **importer** - Entities bringing data services into the EU
* **distributor** - Data intermediation service providers
* **authorized\_representative** - EU representatives for non-EU providers
* **notified\_body** - Bodies for monitoring data intermediation services
* **market\_surveillance** - National competent authorities
* **consumer** - Data subjects and data holders
## Requirement Types
Requirements in the DGA are classified as:
* **obligation** - Mandatory requirements for data intermediaries
* **prohibition** - Restrictions on data use and governance practices
* **right** - Rights of data holders and data users
* **procedure** - Registration and notification procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all DGA articles
curl https://api.law4devs.eu/v1/frameworks/dga/articles
# Get DGA framework details
curl https://api.law4devs.eu/v1/frameworks/dga
# Get DGA requirements
curl https://api.law4devs.eu/v1/frameworks/dga/requirements
# Get DGA recitals
curl https://api.law4devs.eu/v1/frameworks/dga/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all DGA articles
articles = client.articles.list("dga")
# Get DGA requirements by stakeholder role
requirements = client.requirements.list("dga", role="manufacturer")
# Search DGA articles by tag
articles = client.articles.search("dga", query="data intermediary")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all DGA articles
const articles = await client.articles.list('dga');
// Get DGA requirements by stakeholder role
const requirements = await client.requirements.list('dga', { role: 'manufacturer' });
```
## Key Topics
* Re-use of public sector data (with restrictions)
* Data intermediation services and neutrality requirements
* Data altruism organizations and registration
* European Data Innovation Board
* Cross-sector data governance frameworks
* Technical infrastructure for data sharing
* Protection of commercially sensitive information
# Digital Markets Act (DMA)
Source: https://docs.law4devs.eu/frameworks/dma
Regulation (EU) 2022/1925 on Contestable and Fair Markets in the Digital Sector.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32022R1925 |
| **Status** | Active |
| **Articles** | 54 |
| **Recitals** | 109 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R1925) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | ------------------------------------------- |
| 2022-10-01 | Entry into force | Regulation enters into force |
| 2023-07-03 | Partial application | Gatekeeper designation application deadline |
## Stakeholder Roles
The DMA defines obligations for the following stakeholder roles:
* **manufacturer** - Core platform service providers and gatekeepers
* **importer** - Entities bringing core platform services into the EU
* **distributor** - Business users of core platform services
* **authorized\_representative** - EU representatives for non-EU gatekeepers
* **notified\_body** - Bodies for compliance function verification
* **market\_surveillance** - European Commission (enforcement authority)
* **consumer** - End users of core platform services
## Requirement Types
Requirements in the DMA are classified as:
* **obligation** - Do's and don'ts for gatekeepers (Articles 5, 6, 7)
* **prohibition** - Prohibited practices for gatekeepers
* **right** - Rights of business users and end users
* **procedure** - Gatekeeper designation and compliance procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all DMA articles
curl https://api.law4devs.eu/v1/frameworks/dma/articles
# Get DMA framework details
curl https://api.law4devs.eu/v1/frameworks/dma
# Get DMA requirements
curl https://api.law4devs.eu/v1/frameworks/dma/requirements
# Get DMA recitals
curl https://api.law4devs.eu/v1/frameworks/dma/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all DMA articles
articles = client.articles.list("dma")
# Get DMA requirements by stakeholder role
requirements = client.requirements.list("dma", role="manufacturer")
# Search DMA articles by tag
articles = client.articles.search("dma", query="gatekeeper")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all DMA articles
const articles = await client.articles.list('dma');
// Get DMA requirements by stakeholder role
const requirements = await client.requirements.list('dma', { role: 'manufacturer' });
```
## Key Topics
* Gatekeeper designation criteria (size, users, entrenched position)
* Do's and don'ts for gatekeepers (Articles 5, 6, 7)
* Interoperability and data portability requirements
* Self-preferencing restrictions
* Business user rights and fair treatment
* End user choice and switching rights
* Market investigations and enforcement
* Fines and periodic penalty payments
# Digital Operational Resilience Act (DORA)
Source: https://docs.law4devs.eu/frameworks/dora
Regulation (EU) 2022/2554 on digital operational resilience for the financial sector.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32022R2554 |
| **Status** | Active |
| **Articles** | 64 |
| **Recitals** | 106 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R2554) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------ |
| 2023-01-16 | Entry into force | Regulation enters into force |
| 2025-01-17 | Full application | Full application of all requirements |
## Stakeholder Roles
DORA defines obligations for the following stakeholder roles:
* **manufacturer** - ICT service providers and tool developers
* **importer** - Entities importing ICT services into the EU
* **distributor** - Financial entities using ICT services
* **authorized\_representative** - EU representatives for third-country providers
* **notified\_body** - Oversight bodies for critical ICT providers
* **market\_surveillance** - Competent authorities and ESAs
* **consumer** - Financial service end users
## Requirement Types
Requirements in DORA are classified as:
* **obligation** - Mandatory ICT risk management requirements
* **prohibition** - Restricted practices and dependencies
* **right** - Rights of financial entities and users
* **procedure** - Incident reporting and testing procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all DORA articles
curl https://api.law4devs.eu/v1/frameworks/dora/articles
# Get DORA framework details
curl https://api.law4devs.eu/v1/frameworks/dora
# Get DORA requirements
curl https://api.law4devs.eu/v1/frameworks/dora/requirements
# Get DORA recitals
curl https://api.law4devs.eu/v1/frameworks/dora/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all DORA articles
articles = client.articles.list("dora")
# Get DORA requirements by stakeholder role
requirements = client.requirements.list("dora", role="manufacturer")
# Search DORA articles by tag
articles = client.articles.search("dora", query="ICT risk")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all DORA articles
const articles = await client.articles.list('dora');
// Get DORA requirements by stakeholder role
const requirements = await client.requirements.list('dora', { role: 'manufacturer' });
```
## Key Topics
* ICT risk management framework
* ICT-related incident reporting
* Digital operational resilience testing
* ICT third-party risk management
* Information and intelligence sharing
* Critical ICT third-party provider oversight
# Digital Services Act (DSA)
Source: https://docs.law4devs.eu/frameworks/dsa
Regulation (EU) 2022/2065 on a Single Market For Digital Services.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32022R2065 |
| **Status** | Active |
| **Articles** | 93 |
| **Recitals** | 156 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022R2065) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | -------------------------------------- |
| 2022-11-16 | Entry into force | Regulation enters into force |
| 2023-08-25 | Partial application | Obligations for VLOPs and VLOSEs apply |
| 2024-02-17 | Full application | Full application of all requirements |
## Stakeholder Roles
The DSA defines obligations for the following stakeholder roles:
* **manufacturer** - Providers of intermediary services and platforms
* **importer** - Entities bringing digital services into the EU
* **distributor** - Hosting and caching service providers
* **authorized\_representative** - EU representatives for non-EU providers
* **notified\_body** - Bodies for out-of-court dispute settlement
* **market\_surveillance** - Digital Services Coordinators and Commission
* **consumer** - Recipients of digital services (users)
## Requirement Types
Requirements in the DSA are classified as:
* **obligation** - Mandatory requirements for intermediary services
* **prohibition** - Restricted practices (e.g., dark patterns, certain advertising)
* **right** - Rights of service recipients (users)
* **procedure** - Notice and action procedures, complaint handling
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all DSA articles
curl https://api.law4devs.eu/v1/frameworks/dsa/articles
# Get DSA framework details
curl https://api.law4devs.eu/v1/frameworks/dsa
# Get DSA requirements
curl https://api.law4devs.eu/v1/frameworks/dsa/requirements
# Get DSA recitals
curl https://api.law4devs.eu/v1/frameworks/dsa/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all DSA articles
articles = client.articles.list("dsa")
# Get DSA requirements by stakeholder role
requirements = client.requirements.list("dsa", role="manufacturer")
# Search DSA articles by tag
articles = client.articles.search("dsa", query="content moderation")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all DSA articles
const articles = await client.articles.list('dsa');
// Get DSA requirements by stakeholder role
const requirements = await client.requirements.list('dsa', { role: 'manufacturer' });
```
## Key Topics
* Intermediary service liability and safe harbors
* Content moderation and notice-and-action mechanisms
* Online platform transparency and reporting
* Very Large Online Platforms (VLOPs) additional obligations
* Very Large Online Search Engines (VLOSEs) obligations
* Advertising transparency and restrictions
* Dark patterns and user interface manipulation
* Risk assessments and independent auditing
# eIDAS Regulation
Source: https://docs.law4devs.eu/frameworks/eidas
Regulation (EU) 910/2014 on electronic identification and trust services for electronic transactions in the internal market.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32014R0910 |
| **Status** | Active |
| **Articles** | 52 |
| **Recitals** | 77 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32014R0910) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ---------------------------- |
| 2014-09-17 | Entry into force | Regulation enters into force |
## Stakeholder Roles
The eIDAS Regulation defines obligations for the following stakeholder roles:
* **manufacturer** - Developers of trust service solutions and eID systems
* **importer** - Entities bringing trust services into the EU
* **distributor** - Trust service providers making services available
* **authorized\_representative** - Representatives for non-EU trust service providers
* **notified\_body** - Conformity assessment bodies for qualified trust services
* **market\_surveillance** - National supervisory bodies
* **consumer** - Users of trust services and eID
## Requirement Types
Requirements in eIDAS are classified as:
* **obligation** - Mandatory requirements for trust service providers
* **prohibition** - Restricted practices for trust services
* **right** - Rights of trust service users
* **procedure** - Procedures for notification and supervision
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all eIDAS articles
curl https://api.law4devs.eu/v1/frameworks/eidas/articles
# Get eIDAS framework details
curl https://api.law4devs.eu/v1/frameworks/eidas
# Get eIDAS requirements
curl https://api.law4devs.eu/v1/frameworks/eidas/requirements
# Get eIDAS recitals
curl https://api.law4devs.eu/v1/frameworks/eidas/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all eIDAS articles
articles = client.articles.list("eidas")
# Get eIDAS requirements by stakeholder role
requirements = client.requirements.list("eidas", role="manufacturer")
# Search eIDAS articles by tag
articles = client.articles.search("eidas", query="trust service")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all eIDAS articles
const articles = await client.articles.list('eidas');
// Get eIDAS requirements by stakeholder role
const requirements = await client.requirements.list('eidas', { role: 'manufacturer' });
```
## Key Topics
* Electronic identification schemes (eID)
* Qualified trust services
* Electronic signatures (advanced, qualified)
* Electronic seals and timestamps
* Website authentication certificates
* Electronic registered delivery services
* Cross-border recognition and interoperability
# European Digital Identity Regulation (eIDAS 2.0)
Source: https://docs.law4devs.eu/frameworks/eidas2
Regulation (EU) 2024/1183 amending eIDAS — European Digital Identity.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32024R1183 |
| **Status** | Active |
| **Articles** | 2 |
| **Recitals** | 78 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32024R1183) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | --------------------------------------------------- |
| 2024-05-20 | Entry into force | Regulation enters into force |
| 2026-11-21 | Partial application | EUDI Wallet must be made available by Member States |
## Stakeholder Roles
The eIDAS 2.0 Regulation defines obligations for the following stakeholder roles:
* **manufacturer** - Developers of EUDI Wallet solutions and components
* **importer** - Entities bringing wallet solutions into the EU
* **distributor** - Providers of wallet services and relying parties
* **authorized\_representative** - EU representatives for non-EU providers
* **notified\_body** - Conformity assessment bodies for wallet components
* **market\_surveillance** - National supervisory bodies
* **consumer** - EU citizens and residents using digital identity wallets
## Requirement Types
Requirements in eIDAS 2.0 are classified as:
* **obligation** - Mandatory requirements for wallet providers and relying parties
* **prohibition** - Restricted practices for identity verification
* **right** - Rights of wallet users (access, portability, privacy)
* **procedure** - Certification and notification procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all eIDAS 2.0 articles
curl https://api.law4devs.eu/v1/frameworks/eidas2/articles
# Get eIDAS 2.0 framework details
curl https://api.law4devs.eu/v1/frameworks/eidas2
# Get eIDAS 2.0 requirements
curl https://api.law4devs.eu/v1/frameworks/eidas2/requirements
# Get eIDAS 2.0 recitals
curl https://api.law4devs.eu/v1/frameworks/eidas2/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all eIDAS 2.0 articles
articles = client.articles.list("eidas2")
# Get eIDAS 2.0 requirements by stakeholder role
requirements = client.requirements.list("eidas2", role="manufacturer")
# Search eIDAS 2.0 articles by tag
articles = client.articles.search("eidas2", query="wallet")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all eIDAS 2.0 articles
const articles = await client.articles.list('eidas2');
// Get eIDAS 2.0 requirements by stakeholder role
const requirements = await client.requirements.list('eidas2', { role: 'manufacturer' });
```
## Key Topics
* European Digital Identity Wallet (EUDI Wallet)
* Mandatory acceptance by public services and large platforms
* Wallet components and certification
* Person identification data and attributes
* Privacy and data minimization requirements
* Cross-border interoperability
* User consent and control mechanisms
* Security and fraud prevention
# ePrivacy Directive (ePD)
Source: https://docs.law4devs.eu/frameworks/eprivacy
Directive 2002/58/EC on Privacy and Electronic Communications (as amended).
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32002L0058 |
| **Status** | Active |
| **Articles** | 21 |
| **Recitals** | 48 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32002L0058) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------------------------- |
| 2002-07-31 | Entry into force | Directive enters into force (multiple amendments since) |
## Stakeholder Roles
The ePrivacy Directive defines obligations for the following stakeholder roles:
* **manufacturer** - Developers of cookies, tracking technologies, and communication tools
* **importer** - Entities bringing electronic communication services into the EU
* **distributor** - Providers of publicly available electronic communications services
* **authorized\_representative** - EU representatives for non-EU service providers
* **notified\_body** - Bodies for communications service certification
* **market\_surveillance** | National regulatory authorities
* **consumer** - Users of electronic communications services
## Requirement Types
Requirements in the ePrivacy Directive are classified as:
* **obligation** - Mandatory requirements for electronic communications providers
* **prohibition** - Prohibited practices (unsolicited communications, unauthorized access)
* **right** - Rights of subscribers and users
* **procedure** - Consent and notification procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all ePrivacy Directive articles
curl https://api.law4devs.eu/v1/frameworks/eprivacy/articles
# Get ePrivacy Directive framework details
curl https://api.law4devs.eu/v1/frameworks/eprivacy
# Get ePrivacy Directive requirements
curl https://api.law4devs.eu/v1/frameworks/eprivacy/requirements
# Get ePrivacy Directive recitals
curl https://api.law4devs.eu/v1/frameworks/eprivacy/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all ePrivacy Directive articles
articles = client.articles.list("eprivacy")
# Get ePrivacy Directive requirements by stakeholder role
requirements = client.requirements.list("eprivacy", role="manufacturer")
# Search ePrivacy Directive articles by tag
articles = client.articles.search("eprivacy", query="cookie consent")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all ePrivacy Directive articles
const articles = await client.articles.list('eprivacy');
// Get ePrivacy Directive requirements by stakeholder role
const requirements = await client.requirements.list('eprivacy', { role: 'manufacturer' });
```
## Key Topics
* Cookie consent requirements (the "cookie law")
* Confidentiality of communications
* Traffic and location data restrictions
* Unsolicited communications (spam, cold calls)
* Security of networks and services
* Caller/called line identification restrictions
* Relationship with GDPR (lex specialis)
# General Data Protection Regulation (GDPR)
Source: https://docs.law4devs.eu/frameworks/gdpr
Regulation (EU) 2016/679 on the protection of natural persons with regard to the processing of personal data.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32016R0679 |
| **Status** | Active |
| **Articles** | 99 |
| **Recitals** | 173 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016R0679) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------ |
| 2016-05-24 | Entry into force | Regulation enters into force |
| 2018-05-25 | Full application | Full application of all requirements |
## Stakeholder Roles
The GDPR defines obligations for the following stakeholder roles:
* **manufacturer** - Developers of data processing systems and tools
* **importer** - Entities bringing data processing services into the EU
* **distributor** - Processors making data services available
* **authorized\_representative** - EU representatives for non-EU controllers/processors
* **notified\_body** - Certification bodies for data protection
* **market\_surveillance** - Supervisory authorities (DPAs)
* **consumer** - Data subjects (natural persons)
## Requirement Types
Requirements in the GDPR are classified as:
* **obligation** - Mandatory data protection requirements
* **prohibition** - Processing activities that are forbidden
* **right** - Data subject rights (access, rectification, erasure, etc.)
* **procedure** - Procedures for compliance and breach notification
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all GDPR articles
curl https://api.law4devs.eu/v1/frameworks/gdpr/articles
# Get GDPR framework details
curl https://api.law4devs.eu/v1/frameworks/gdpr
# Get GDPR requirements
curl https://api.law4devs.eu/v1/frameworks/gdpr/requirements
# Get GDPR recitals
curl https://api.law4devs.eu/v1/frameworks/gdpr/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all GDPR articles
articles = client.articles.list("gdpr")
# Get GDPR requirements by stakeholder role
requirements = client.requirements.list("gdpr", role="manufacturer")
# Search GDPR articles by tag
articles = client.articles.search("gdpr", query="consent")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all GDPR articles
const articles = await client.articles.list('gdpr');
// Get GDPR requirements by stakeholder role
const requirements = await client.requirements.list('gdpr', { role: 'manufacturer' });
```
## Key Topics
* Lawful basis for processing
* Data subject rights (access, rectification, erasure, portability, objection)
* Controller and processor obligations
* Data protection impact assessments
* Breach notification requirements
* International data transfers
* Administrative fines and penalties
# Markets in Crypto-Assets Regulation (MiCA)
Source: https://docs.law4devs.eu/frameworks/mica
Regulation (EU) 2023/1114 on Markets in Crypto-Assets.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32023R1114 |
| **Status** | Active |
| **Articles** | 149 |
| **Recitals** | 119 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32023R1114) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | ------------------------------------------------------ |
| 2023-06-29 | Entry into force | Regulation enters into force |
| 2024-06-30 | Partial application | Asset-referenced tokens and e-money tokens rules apply |
| 2024-12-30 | Full application | Full application of all requirements |
## Stakeholder Roles
MiCA defines obligations for the following stakeholder roles:
* **manufacturer** - Issuers of crypto-assets and CASP developers
* **importer** - Crypto-asset service providers from third countries
* **distributor** - Trading platforms and exchanges
* **authorized\_representative** - EU representatives for non-EU issuers
* **notified\_body** - Bodies for crypto-asset service certification
* **market\_surveillance** - National competent authorities and ESMA
* **consumer** - Holders and users of crypto-assets
## Requirement Types
Requirements in MiCA are classified as:
* **obligation** - Mandatory requirements for issuers and CASPs
* **prohibition** - Prohibited market practices (market manipulation, insider trading)
* **right** - Rights of crypto-asset holders
* **procedure** - Authorization and whitepaper notification procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all MiCA articles
curl https://api.law4devs.eu/v1/frameworks/mica/articles
# Get MiCA framework details
curl https://api.law4devs.eu/v1/frameworks/mica
# Get MiCA requirements
curl https://api.law4devs.eu/v1/frameworks/mica/requirements
# Get MiCA recitals
curl https://api.law4devs.eu/v1/frameworks/mica/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all MiCA articles
articles = client.articles.list("mica")
# Get MiCA requirements by stakeholder role
requirements = client.requirements.list("mica", role="manufacturer")
# Search MiCA articles by tag
articles = client.articles.search("mica", query="stablecoin")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all MiCA articles
const articles = await client.articles.list('mica');
// Get MiCA requirements by stakeholder role
const requirements = await client.requirements.list('mica', { role: 'manufacturer' });
```
## Key Topics
* Crypto-asset whitepaper requirements
* Asset-referenced tokens (ARTs) and e-money tokens (EMTs)
* Crypto-asset service providers (CASPs) authorization
* Trading platforms and exchange requirements
* Market abuse prevention (insider trading, market manipulation)
* Consumer protection and disclosure requirements
* Custody and wallet service requirements
* Environmental impact disclosures
# NIS Directive (Original)
Source: https://docs.law4devs.eu/frameworks/nis1
Directive (EU) 2016/1148 on Network and Information Systems Security.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32016L1148 |
| **Status** | Superseded |
| **Articles** | 27 |
| **Recitals** | 75 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016L1148) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ---------------------------- |
| 2016-07-19 | Entry into force | Directive enters into force |
| 2024-10-18 | Repealed | Superseded by NIS2 Directive |
**This directive has been repealed.** NIS1 was superseded by the NIS2 Directive (EU) 2022/2555 as of October 18, 2024. This documentation is provided for historical reference and to understand the evolution of EU cybersecurity law.
## Stakeholder Roles
The NIS Directive defined obligations for the following stakeholder roles:
* **manufacturer** - Providers of digital products and services
* **importer** - Entities operating essential services
* **distributor** - Digital service providers
* **authorized\_representative** - EU representatives for non-EU providers
* **notified\_body** - Bodies for security certification
* **market\_surveillance** - National competent authorities and CSIRTs
* **consumer** - Users of essential and digital services
## Requirement Types
Requirements in NIS1 were classified as:
* **obligation** - Security and notification requirements
* **prohibition** - Restricted security practices
* **right** - Rights of operators and users
* **procedure** - Incident notification procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all NIS1 articles (historical reference)
curl https://api.law4devs.eu/v1/frameworks/nis1/articles
# Get NIS1 framework details
curl https://api.law4devs.eu/v1/frameworks/nis1
# Get NIS1 requirements
curl https://api.law4devs.eu/v1/frameworks/nis1/requirements
# Get NIS1 recitals
curl https://api.law4devs.eu/v1/frameworks/nis1/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all NIS1 articles (historical reference)
articles = client.articles.list("nis1")
# Get NIS1 requirements by stakeholder role
requirements = client.requirements.list("nis1", role="manufacturer")
# Search NIS1 articles by tag
articles = client.articles.search("nis1", query="essential services")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all NIS1 articles (historical reference)
const articles = await client.articles.list('nis1');
// Get NIS1 requirements by stakeholder role
const requirements = await client.requirements.list('nis1', { role: 'manufacturer' });
```
## Key Topics (Historical)
* Operators of Essential Services (OES) identification
* Digital Service Providers (DSPs) requirements
* Security and notification requirements
* National cybersecurity strategies
* Cooperation and information sharing mechanisms
* Network of CSIRTs (Computer Security Incident Response Teams)
* Transition to NIS2 (expanded scope, harmonized requirements)
# NIS2 Directive
Source: https://docs.law4devs.eu/frameworks/nis2
Directive (EU) 2022/2555 on measures for a high common level of cybersecurity across the Union.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32022L2555 |
| **Status** | Active |
| **Articles** | 46 |
| **Recitals** | 144 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32022L2555) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ---------------- | ------------------------------------ |
| 2022-12-27 | Entry into force | Directive enters into force |
| 2024-10-17 | Transposition | Member States transposition deadline |
## Stakeholder Roles
The NIS2 Directive defines obligations for the following stakeholder roles:
* **manufacturer** - Providers of digital products and services
* **importer** - Entities importing digital services into the EU
* **distributor** - Service providers in the supply chain
* **authorized\_representative** - EU representatives for non-EU entities
* **notified\_body** - Designated conformity assessment bodies
* **market\_surveillance** - National competent authorities
* **consumer** - Users of essential and important services
## Requirement Types
Requirements in NIS2 are classified as:
* **obligation** - Mandatory cybersecurity risk management measures
* **prohibition** - Restricted practices and non-compliant behaviors
* **right** - Rights of entities and users
* **procedure** - Incident reporting and notification procedures
* **general** - General provisions and scope definitions
## API Usage
```bash theme={null}
# Get all NIS2 articles
curl https://api.law4devs.eu/v1/frameworks/nis2/articles
# Get NIS2 framework details
curl https://api.law4devs.eu/v1/frameworks/nis2
# Get NIS2 requirements
curl https://api.law4devs.eu/v1/frameworks/nis2/requirements
# Get NIS2 recitals
curl https://api.law4devs.eu/v1/frameworks/nis2/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all NIS2 articles
articles = client.articles.list("nis2")
# Get NIS2 requirements by stakeholder role
requirements = client.requirements.list("nis2", role="manufacturer")
# Search NIS2 articles by tag
articles = client.articles.search("nis2", query="incident reporting")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all NIS2 articles
const articles = await client.articles.list('nis2');
// Get NIS2 requirements by stakeholder role
const requirements = await client.requirements.list('nis2', { role: 'manufacturer' });
```
## Key Topics
* Essential and important entity classification
* Cybersecurity risk management measures
* Incident reporting obligations (early warning, incident notification, final report)
* Supply chain security requirements
* Management body liability
* Information sharing and cooperation
# Payment Services Directive 2 (PSD2)
Source: https://docs.law4devs.eu/frameworks/psd2
Directive (EU) 2015/2366 on Payment Services in the Internal Market.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32015L2366 |
| **Status** | Active |
| **Articles** | 117 |
| **Recitals** | 113 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32015L2366) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | ----------------------------------------------------- |
| 2016-01-13 | Entry into force | Directive enters into force |
| 2018-01-13 | Transposition | Member States transposition deadline |
| 2021-01-01 | Partial application | Strong Customer Authentication for e-commerce applies |
## Stakeholder Roles
The PSD2 defines obligations for the following stakeholder roles:
* **manufacturer** - Developers of payment systems and APIs
* **importer** - Payment service providers operating cross-border
* **distributor** - Account servicing payment service providers
* **authorized\_representative** - Agents and branches of payment institutions
* **notified\_body** - Bodies for payment service certification
* **market\_surveillance** - National competent authorities
* **consumer** - Payment service users (payers and payees)
## Requirement Types
Requirements in PSD2 are classified as:
* **obligation** - Mandatory requirements for payment service providers
* **prohibition** - Restricted practices (e.g., surcharging)
* **right** - Rights of payment service users
* **procedure** - Authorization and passporting procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all PSD2 articles
curl https://api.law4devs.eu/v1/frameworks/psd2/articles
# Get PSD2 framework details
curl https://api.law4devs.eu/v1/frameworks/psd2
# Get PSD2 requirements
curl https://api.law4devs.eu/v1/frameworks/psd2/requirements
# Get PSD2 recitals
curl https://api.law4devs.eu/v1/frameworks/psd2/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all PSD2 articles
articles = client.articles.list("psd2")
# Get PSD2 requirements by stakeholder role
requirements = client.requirements.list("psd2", role="manufacturer")
# Search PSD2 articles by tag
articles = client.articles.search("psd2", query="SCA")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all PSD2 articles
const articles = await client.articles.list('psd2');
// Get PSD2 requirements by stakeholder role
const requirements = await client.requirements.list('psd2', { role: 'manufacturer' });
```
## Key Topics
* Strong Customer Authentication (SCA) requirements
* Account Information Services (AIS)
* Payment Initiation Services (PIS)
* Access to Account (XS2A) and API requirements
* Payment service provider licensing
* Consumer protection and liability rules
* Transaction security and communication requirements
* Surcharge prohibition
# Radio Equipment Directive (RED)
Source: https://docs.law4devs.eu/frameworks/red
Directive 2014/53/EU on Radio Equipment.
## Overview
| Field | Value |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **CELEX** | 32014L0053 |
| **Status** | Active |
| **Articles** | 52 |
| **Recitals** | 75 |
| **EUR-Lex** | [View Official Text](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32014L0053) |
## Compliance Deadlines
| Date | Type | Description |
| ---------- | ------------------- | -------------------------------------------------- |
| 2014-05-22 | Entry into force | Directive enters into force |
| 2016-06-13 | Full application | Full application of all requirements |
| 2025-08-01 | Partial application | Article 3(3)(d-f) cybersecurity requirements apply |
## Stakeholder Roles
The RED defines obligations for the following stakeholder roles:
* **manufacturer** - Manufacturers of radio equipment and wireless devices
* **importer** - Entities placing radio equipment on the EU market
* **distributor** - Entities making radio equipment available in the supply chain
* **authorized\_representative** - EU representatives for non-EU manufacturers
* **notified\_body** - Conformity assessment bodies for radio equipment
* **market\_surveillance** - National market surveillance authorities
* **consumer** - Users of radio equipment and wireless devices
## Requirement Types
Requirements in RED are classified as:
* **obligation** - Mandatory essential requirements for radio equipment
* **prohibition** - Restricted equipment and practices
* **right** - Rights of economic operators and users
* **procedure** - Conformity assessment and CE marking procedures
* **general** - General provisions and definitions
## API Usage
```bash theme={null}
# Get all RED articles
curl https://api.law4devs.eu/v1/frameworks/red/articles
# Get RED framework details
curl https://api.law4devs.eu/v1/frameworks/red
# Get RED requirements
curl https://api.law4devs.eu/v1/frameworks/red/requirements
# Get RED recitals
curl https://api.law4devs.eu/v1/frameworks/red/recitals
```
```python theme={null}
# Python SDK
from law4devs import Law4Devs
client = Law4Devs(api_key="your-api-key")
# List all RED articles
articles = client.articles.list("red")
# Get RED requirements by stakeholder role
requirements = client.requirements.list("red", role="manufacturer")
# Search RED articles by tag
articles = client.articles.search("red", query="cybersecurity")
```
```typescript theme={null}
// TypeScript SDK
import { Law4Devs } from '@law4devs/sdk';
const client = new Law4Devs({ apiKey: 'your-api-key' });
// List all RED articles
const articles = await client.articles.list('red');
// Get RED requirements by stakeholder role
const requirements = await client.requirements.list('red', { role: 'manufacturer' });
```
## Key Topics
* Essential requirements for radio equipment
* Health and safety requirements (Article 3(1)(a))
* EMC requirements (Article 3(1)(b))
* Efficient use of radio spectrum (Article 3(2))
* Cybersecurity requirements for connected devices (Article 3(3)(d-f))
* CE marking and conformity assessment
* Interoperability and accessory requirements
* Software and firmware update requirements
#
Source: https://docs.law4devs.eu/index
Law4Devs
Structured EU regulatory compliance data for developers
## What is Law4Devs?
Law4Devs is a REST API and SDKs that turns EU regulatory texts into structured, queryable JSON. Instead of reading 300-page PDF regulations, your application queries articles, recitals, requirements, compliance deadlines, and tags via simple HTTP calls.
**19 EU frameworks** — CRA, GDPR, NIS2, DORA, AI Act, eIDAS 2.0, DSA, DMA, Data Act, MiCA, and more.
Zero-dependency. `pip install law4devs`
Zero-dependency. `npm install law4devs`
Composer package. `composer require law4devs/law4devs`
Zero-dependency. Maven · Java 17+
pub.dev · Dart 3+ · Flutter & CLI
crates.io · Rust 1.85+ · Tokio async
Interactive docs for all 21 endpoints.
All 19 EU regulations, with CELEX numbers.
# Quick Start
Source: https://docs.law4devs.eu/quickstart
Make your first Law4Devs API call in under 60 seconds.
Pick your preferred method:
```bash theme={null}
# List all frameworks
curl -H "X-API-Key: YOUR_API_KEY" \
https://api.law4devs.eu/v1/frameworks
# Get GDPR articles
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.law4devs.eu/v1/frameworks/gdpr/articles?per_page=5"
# Search across all frameworks
curl -H "X-API-Key: YOUR_API_KEY" \
"https://api.law4devs.eu/v1/search?q=data+breach+notification"
```
```bash theme={null}
pip install law4devs
```
```python theme={null}
import os
from law4devs import Law4DevsClient
client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"])
# List all frameworks
page = client.frameworks.list()
for fw in page.data:
print(fw.slug, fw.name)
# Iterate all GDPR articles (auto-pagination)
for article in client.articles.iter("gdpr"):
print(article.article_number, article.title)
# Search
results = client.search.query("data breach notification")
print(f"Found {results.meta.total} results")
```
```bash theme={null}
npm install law4devs
```
```typescript theme={null}
import { Law4DevsClient } from 'law4devs';
const client = new Law4DevsClient({
apiKey: process.env.LAW4DEVS_API_KEY!,
});
// List all frameworks
const page = await client.frameworks.list();
page.data.forEach(fw => console.log(fw.slug, fw.name));
// Iterate all GDPR articles (auto-pagination)
for await (const article of client.articles.iter('gdpr')) {
console.log(article.articleNumber, article.title);
}
// Search
const results = await client.search.query('data breach notification');
console.log(`Found ${results.meta.total} results`);
```
## Next Steps
# Annexes
Source: https://docs.law4devs.eu/sdks/flutter/annexes
Access regulatory annexes with the Flutter SDK.
## Overview
Annexes are supplementary legal documents attached to EU regulations. They often contain technical requirements, classification criteria, and reference tables.
***
## Methods
### `list()`
```dart theme={null}
Future> list(String frameworkSlug, {int? page, int? perPage})
```
### `get()`
```dart theme={null}
Future get(String frameworkSlug, String annexNumber)
```
### `iter()`
```dart theme={null}
Stream iter(String frameworkSlug, {int perPage = 20})
```
***
## Models
### `AnnexSummary`
| Field | Type | Description |
| --------------- | --------- | ----------------------------- |
| `id` | `int` | Internal ID |
| `frameworkSlug` | `String` | Parent framework |
| `number` | `String` | Annex number (e.g. `I`, `II`) |
| `title` | `String?` | Optional title |
| `position` | `int` | Ordering position |
### `Annex`
Extends `AnnexSummary` with:
| Field | Type | Description |
| --------- | -------- | ------------------ |
| `content` | `String` | Full annex content |
***
## Examples
### List CRA annexes
```dart theme={null}
final page = await client.annexes.list('cra');
for (final a in page.data) {
print('Annex ${a.number}: ${a.title}');
}
```
### Get a specific annex
```dart theme={null}
final annex = await client.annexes.get('cra', 'I');
print(annex.content);
```
### Auto-paginate
```dart theme={null}
await for (final a in client.annexes.iter('ai-act')) {
print('Annex ${a.number}: ${a.title}');
}
```
# Articles
Source: https://docs.law4devs.eu/sdks/flutter/articles
Access regulatory articles with the Flutter SDK.
## Overview
Articles are the core legal text units of each framework. Each article has a number, title, content, paragraphs, and associated tags.
***
## Methods
### `list()`
```dart theme={null}
Future> list(String frameworkSlug, {int? page, int? perPage})
```
### `get()`
```dart theme={null}
Future get(String frameworkSlug, String articleNumber)
```
### `related()`
```dart theme={null}
Future> related(String frameworkSlug, String articleNumber, {int? page, int? perPage})
```
### `iter()`
```dart theme={null}
Stream iter(String frameworkSlug, {int perPage = 20})
```
***
## Models
### `ArticleSummary`
| Field | Type | Description |
| ---------------- | ----------- | ------------------------------ |
| `id` | `int` | Internal ID |
| `frameworkSlug` | `String` | Parent framework (e.g. `gdpr`) |
| `number` | `String` | Article number (e.g. `5`) |
| `title` | `String` | Article title |
| `position` | `int` | Ordering position |
| `paragraphCount` | `int` | Number of paragraphs |
| `tags` | `List` | Semantic tags |
### `Article`
Extends `ArticleSummary` with:
| Field | Type | Description |
| ------------ | ------------------------ | --------------------- |
| `content` | `String` | Full text content |
| `paragraphs` | `List` | Structured paragraphs |
### `ArticleParagraph`
| Field | Type | Description |
| -------------- | -------- | ------------------------------------ |
| `paragraphRef` | `String` | Paragraph reference (e.g. `1`, `1a`) |
| `content` | `String` | Paragraph text |
| `position` | `int` | Ordering position |
***
## Examples
### List articles for a framework
```dart theme={null}
final page = await client.articles.list('gdpr');
print('${page.meta.total} GDPR articles');
for (final a in page.data) {
print('Art. ${a.number}: ${a.title}');
}
```
### Get a specific article
```dart theme={null}
final art5 = await client.articles.get('gdpr', '5');
print(art5.content);
for (final p in art5.paragraphs) {
print('[${p.paragraphRef}] ${p.content}');
}
```
### Get related articles
```dart theme={null}
final related = await client.articles.related('gdpr', '5');
for (final a in related.data) {
print('Related: Art. ${a.number} — ${a.title}');
}
```
### Auto-paginate all articles
```dart theme={null}
await for (final a in client.articles.iter('cra')) {
print('Art. ${a.number}: ${a.title} (${a.paragraphCount} paragraphs)');
}
```
# Compliance Deadlines
Source: https://docs.law4devs.eu/sdks/flutter/compliance
Access compliance deadlines and enforcement dates with the Flutter SDK.
## Overview
Compliance deadlines track enforcement dates, transition periods, and regulatory milestones across all EU frameworks.
***
## Methods
### `deadlines()`
```dart theme={null}
Future> deadlines({String? frameworkSlug, int? page, int? perPage})
```
### `iterDeadlines()`
```dart theme={null}
Stream iterDeadlines({String? frameworkSlug, int perPage = 20})
```
***
## Model
### `ComplianceDeadline`
| Field | Type | Description |
| --------------- | --------- | ------------------ |
| `id` | `int` | Internal ID |
| `frameworkSlug` | `String` | Source framework |
| `title` | `String` | Deadline name |
| `description` | `String?` | Detail description |
| `deadline` | `String` | ISO 8601 date |
| `deadlineType` | `String` | Type of deadline |
***
## Examples
### All upcoming deadlines
```dart theme={null}
await for (final d in client.compliance.iterDeadlines()) {
print('[${d.frameworkSlug.toUpperCase()}] ${d.deadline} — ${d.title}');
}
```
### NIS2-specific deadlines
```dart theme={null}
await for (final d in client.compliance.iterDeadlines(frameworkSlug: 'nis2')) {
print('${d.deadline}: ${d.title}');
}
```
### Paginated deadlines
```dart theme={null}
final page = await client.compliance.deadlines(frameworkSlug: 'dora');
print('${page.meta.total} DORA deadlines');
for (final d in page.data) {
print('${d.deadline} — ${d.title}');
}
```
# Error Handling
Source: https://docs.law4devs.eu/sdks/flutter/error-handling
Handle API errors gracefully with the Flutter SDK.
## Exception Hierarchy
```
Law4DevsException (base)
├── NotFoundError (404)
├── ValidationError (400)
├── RateLimitError (429)
└── ServerError (5xx)
```
All exceptions implement Dart's `Exception` interface and expose `message` and `statusCode`.
***
## Catching Errors
```dart theme={null}
import 'package:law4devs/law4devs.dart';
try {
final fw = await client.frameworks.get('nonexistent');
} on NotFoundError catch (e) {
print('404: ${e.message}');
} on ValidationError catch (e) {
print('400: ${e.message}');
} on RateLimitError catch (e) {
print('429: Rate limited — SDK retried automatically');
} on ServerError catch (e) {
print('${e.statusCode}: ${e.message}');
} on Law4DevsException catch (e) {
print('API error ${e.statusCode}: ${e.message}');
}
```
***
## Retry Behavior
The SDK automatically retries `429` and `5xx` responses with exponential backoff:
| Attempt | Delay |
| --------- | --------- |
| 1st retry | 1 second |
| 2nd retry | 2 seconds |
| 3rd retry | 4 seconds |
`404` and `400` are thrown immediately without retrying.
***
## Safe Get Pattern
```dart theme={null}
Future safeGet(Law4DevsClient client, String slug) async {
try {
return await client.frameworks.get(slug);
} on NotFoundError {
return null;
}
}
final fw = await safeGet(client, 'unknown-slug');
if (fw != null) {
print(fw.name);
}
```
***
## Error Properties
| Property | Type | Description |
| ------------ | -------- | -------------------------- |
| `message` | `String` | Error message from the API |
| `statusCode` | `int?` | HTTP status code |
# Frameworks
Source: https://docs.law4devs.eu/sdks/flutter/frameworks
List, retrieve, and iterate EU regulatory frameworks with the Flutter SDK.
## Overview
The `frameworks` resource provides access to all 19 EU regulatory frameworks — GDPR, CRA, NIS2, AI Act, DORA, and more.
***
## Methods
### `list()`
```dart theme={null}
Future> list({int? page, int? perPage})
```
**Parameters**
| Parameter | Type | Default | Description |
| --------- | ------ | ------- | -------------- |
| `page` | `int?` | `1` | Page number |
| `perPage` | `int?` | `20` | Items per page |
### `get()`
```dart theme={null}
Future get(String slug)
```
### `iter()`
```dart theme={null}
Stream iter({int perPage = 20})
```
Lazy stream — fetches the next page only when the current page is exhausted.
***
## Models
### `Framework`
| Field | Type | Description |
| -------------- | --------- | --------------------------------- |
| `id` | `int` | Internal ID |
| `slug` | `String` | URL-safe identifier (e.g. `gdpr`) |
| `name` | `String` | Full regulation name |
| `shortName` | `String` | Abbreviation (e.g. `GDPR`) |
| `celexNumber` | `String` | EUR-Lex CELEX identifier |
| `description` | `String?` | Short description |
| `isActive` | `bool` | Whether actively maintained |
| `status` | `String` | `active` or `superseded` |
| `articleCount` | `int` | Number of scraped articles |
| `recitalCount` | `int` | Number of scraped recitals |
| `createdAt` | `String` | ISO 8601 timestamp |
### `FrameworkDetail`
Extends `Framework` with:
| Field | Type | Description |
| ------------------ | -------- | ------------------------- |
| `eurLexUrl` | `String` | Link to EUR-Lex source |
| `requirementCount` | `int` | Number of requirements |
| `annexCount` | `int` | Number of annexes |
| `tagCount` | `int` | Number of associated tags |
| `coverage` | `Map?` | Coverage statistics |
***
## Examples
### List all frameworks
```dart theme={null}
final page = await client.frameworks.list();
print('${page.meta.total} frameworks');
for (final fw in page.data) {
print('${fw.slug} — ${fw.name} (${fw.articleCount} articles)');
}
```
### Get framework detail
```dart theme={null}
final gdpr = await client.frameworks.get('gdpr');
print('GDPR: ${gdpr.articleCount} articles, ${gdpr.recitalCount} recitals');
print('EUR-Lex: ${gdpr.eurLexUrl}');
```
### Auto-paginate all frameworks
```dart theme={null}
await for (final fw in client.frameworks.iter()) {
if (fw.status == 'active') {
print(fw.name);
}
}
```
### Filter active frameworks
```dart theme={null}
final page = await client.frameworks.list();
final active = page.data.where((fw) => fw.isActive).toList();
print('${active.length} active frameworks');
```
# Installation
Source: https://docs.law4devs.eu/sdks/flutter/installation
Install the Law4Devs Flutter/Dart SDK and make your first API call.
## Requirements
* Dart SDK >=3.0.0
* Works on Flutter (mobile, web, desktop) and Dart CLI
## Install
Add to your `pubspec.yaml`:
```yaml theme={null}
dependencies:
law4devs: ^1.0.0
```
Then run:
```bash theme={null}
dart pub get
```
Or for Flutter projects:
```bash theme={null}
flutter pub get
```
## First Call
```dart theme={null}
import 'package:law4devs/law4devs.dart';
final client = Law4DevsClient(
apiKey: Platform.environment['LAW4DEVS_API_KEY'],
);
final page = await client.frameworks.list();
print(page.data.first.name); // e.g. "Cyber Resilience Act"
```
## Configuration
```dart theme={null}
final client = Law4DevsClient(
apiKey: Platform.environment['LAW4DEVS_API_KEY'],
baseUrl: 'https://api.law4devs.eu/v1', // default
timeout: const Duration(seconds: 30), // default
maxRetries: 3, // retries on 429/5xx
);
```
| Option | Type | Default | Description |
| ------------ | ---------- | ----------------------- | --------------------------------------------------- |
| `apiKey` | `String?` | `null` | Your API key (required for authenticated endpoints) |
| `baseUrl` | `String` | Production URL | Override for dev/staging environment |
| `timeout` | `Duration` | `Duration(seconds: 30)` | Per-request HTTP timeout |
| `maxRetries` | `int` | `3` | Retries on 429/5xx responses |
## Environment Variable
```bash theme={null}
export LAW4DEVS_API_KEY="your-key"
```
```dart theme={null}
final client = Law4DevsClient(
apiKey: Platform.environment['LAW4DEVS_API_KEY'],
);
```
Never hardcode your API key in source code or commit it to version control.
## Verify the Install
```dart theme={null}
// verify_install.dart
import 'package:law4devs/law4devs.dart';
import 'dart:io';
void main() async {
final client = Law4DevsClient(
apiKey: Platform.environment['LAW4DEVS_API_KEY'],
);
final page = await client.frameworks.list();
print('${page.meta.total} frameworks');
}
```
```bash theme={null}
LAW4DEVS_API_KEY=your-key dart run verify_install.dart
# → 19 frameworks
```
# Pagination
Source: https://docs.law4devs.eu/sdks/flutter/pagination
Understand manual and automatic pagination in the Flutter SDK.
## Overview
Every `list()` call returns a `Page`. The `iter()` method returns a `Stream` that lazily auto-paginates across all pages.
***
## The `Page` Object
```dart theme={null}
final page = await client.frameworks.list(page: 1, perPage: 20);
page.data // List — items on this page
page.meta.total // int — total items across all pages
page.meta.page // int — current page number
page.meta.perPage // int — items per page
page.meta.pages // int — total number of pages
page.links.next // String? — URL of next page (null if last page)
page.links.prev // String? — URL of previous page (null if first page)
page.hasNext // bool — true if more pages exist
```
**`PageMeta` fields**
| Field | Type | Description |
| ------------ | -------- | ------------------ |
| `apiVersion` | `String` | API version string |
| `total` | `int` | Total items |
| `page` | `int` | Current page |
| `perPage` | `int` | Items per page |
| `pages` | `int` | Total pages |
***
## Manual Pagination
```dart theme={null}
var page = 1;
while (true) {
final result = await client.frameworks.list(page: page, perPage: 20);
for (final fw in result.data) {
print(fw.name);
}
if (!result.hasNext) break;
page++;
}
```
***
## Automatic Pagination with `iter()`
`iter()` returns a `Stream` — it fetches the next page only when the current page is exhausted:
```dart theme={null}
await for (final fw in client.frameworks.iter()) {
print(fw.name);
}
```
With a custom page size:
```dart theme={null}
await for (final a in client.articles.iter('gdpr', perPage: 50)) {
print(a.title);
}
```
***
## Early Stopping
```dart theme={null}
await for (final fw in client.frameworks.iter()) {
if (fw.slug == 'gdpr') {
print('Found GDPR');
break;
}
}
```
***
## Progress Tracking
```dart theme={null}
var count = 0;
final total = (await client.frameworks.list()).meta.total;
await for (final fw in client.frameworks.iter()) {
count++;
print('[$count/$total] ${fw.name}');
}
```
# Quick Start
Source: https://docs.law4devs.eu/sdks/flutter/quickstart
Make your first Law4Devs API call with Dart/Flutter in under 60 seconds.
## Install
```yaml theme={null}
dependencies:
law4devs: ^1.0.0
```
```bash theme={null}
dart pub get
```
## Basic Usage
```dart theme={null}
import 'package:law4devs/law4devs.dart';
import 'dart:io';
void main() async {
final client = Law4DevsClient(
apiKey: Platform.environment['LAW4DEVS_API_KEY'],
);
// List all frameworks
final page = await client.frameworks.list();
for (final fw in page.data) {
print('${fw.slug} — ${fw.name}');
}
// List first 5 CRA articles
final articles = await client.articles.list('cra', perPage: 5);
for (final a in articles.data) {
print('Art. ${a.number}: ${a.title}');
}
// Auto-paginate all GDPR articles
await for (final a in client.articles.iter('gdpr')) {
print('${a.number} ${a.title}');
}
// Search across all frameworks
final results = await client.search.query('data breach notification');
print('Found ${results.meta.total} results');
}
```
## Next Steps
# Recitals
Source: https://docs.law4devs.eu/sdks/flutter/recitals
Access regulatory recitals (preamble) with the Flutter SDK.
## Overview
Recitals are the preamble sections of EU regulations. They provide context, intent, and interpretive guidance for the articles that follow.
***
## Methods
### `list()`
```dart theme={null}
Future> list(String frameworkSlug, {int? page, int? perPage})
```
### `get()`
```dart theme={null}
Future get(String frameworkSlug, String recitalNumber)
```
### `iter()`
```dart theme={null}
Stream iter(String frameworkSlug, {int perPage = 20})
```
***
## Model
### `Recital`
| Field | Type | Description |
| --------------- | --------- | ----------------- |
| `id` | `int` | Internal ID |
| `frameworkSlug` | `String` | Parent framework |
| `number` | `String` | Recital number |
| `title` | `String?` | Optional title |
| `text` | `String` | Full text content |
| `position` | `int` | Ordering position |
***
## Examples
### List recitals
```dart theme={null}
final page = await client.recitals.list('gdpr');
print('${page.meta.total} GDPR recitals');
```
### Get a specific recital
```dart theme={null}
final r1 = await client.recitals.get('gdpr', '1');
print(r1.text);
```
### Auto-paginate all recitals
```dart theme={null}
await for (final r in client.recitals.iter('nis2')) {
print('Recital ${r.number}: ${r.text.substring(0, 80)}...');
}
```
# Requirements
Source: https://docs.law4devs.eu/sdks/flutter/requirements
Query compliance requirements with the Flutter SDK.
## Overview
Requirements are structured compliance obligations extracted from regulatory articles. Filter by framework to scope results.
***
## Methods
### `list()`
```dart theme={null}
Future> list({String? frameworkSlug, int? page, int? perPage})
```
### `iter()`
```dart theme={null}
Stream iter({String? frameworkSlug, int perPage = 20})
```
***
## Model
### `Requirement`
| Field | Type | Description |
| ----------------- | --------- | ------------------ |
| `id` | `int` | Internal ID |
| `frameworkSlug` | `String` | Source framework |
| `articleNumber` | `String` | Article reference |
| `title` | `String?` | Optional title |
| `description` | `String` | Requirement text |
| `requirementType` | `String` | Obligation type |
| `stakeholder` | `String?` | Target stakeholder |
***
## Examples
### All requirements across all frameworks
```dart theme={null}
await for (final r in client.requirements.iter()) {
print('[${r.frameworkSlug.toUpperCase()}] Art. ${r.articleNumber}: ${r.description}');
}
```
### Requirements for a specific framework
```dart theme={null}
await for (final r in client.requirements.iter(frameworkSlug: 'cra')) {
print('[${r.requirementType}] ${r.description}');
}
```
### Paginated request
```dart theme={null}
final page = await client.requirements.list(frameworkSlug: 'nis2', perPage: 50);
print('${page.meta.total} NIS2 requirements');
```
# Search
Source: https://docs.law4devs.eu/sdks/flutter/search
Full-text search across all EU frameworks with the Flutter SDK.
## Overview
The search resource lets you query articles, recitals, and annexes across all 19 EU frameworks simultaneously. Optionally filter by framework or result type.
***
## Methods
### `query()`
```dart theme={null}
Future> query(
String q, {
String? framework,
String? resultType,
int? page,
int? perPage,
})
```
**Parameters**
| Parameter | Type | Description |
| ------------ | --------- | --------------------------------------------- |
| `q` | `String` | Search query |
| `framework` | `String?` | Filter by framework slug (e.g. `gdpr`) |
| `resultType` | `String?` | Filter by type: `article`, `recital`, `annex` |
| `page` | `int?` | Page number |
| `perPage` | `int?` | Items per page |
### `iter()`
```dart theme={null}
Stream iter(String q, {String? framework, String? resultType, int perPage = 20})
```
***
## Model
### `SearchResult`
| Field | Type | Description |
| --------------- | --------- | -------------------------------- |
| `id` | `int` | Internal ID |
| `frameworkSlug` | `String` | Source framework |
| `type` | `String` | `article`, `recital`, or `annex` |
| `number` | `String` | Item number |
| `title` | `String?` | Item title |
| `matchContext` | `String?` | Surrounding matched text snippet |
***
## Examples
### Basic search
```dart theme={null}
final results = await client.search.query('incident notification 72 hours');
print('Found ${results.meta.total} results');
for (final r in results.data) {
print('[${r.frameworkSlug.toUpperCase()}] ${r.type} ${r.number}: ${r.title}');
}
```
### Search within a specific framework
```dart theme={null}
final results = await client.search.query(
'data breach',
framework: 'gdpr',
);
```
### Filter by type
```dart theme={null}
final results = await client.search.query(
'supply chain security',
resultType: 'article',
);
```
### Auto-paginate search results
```dart theme={null}
await for (final r in client.search.iter('personal data')) {
print('[${r.frameworkSlug}] ${r.number} — ${r.title}');
}
```
# Tags
Source: https://docs.law4devs.eu/sdks/flutter/tags
Access semantic tags with the Flutter SDK.
## Overview
Tags provide semantic classification for articles across all frameworks. Use them to find related articles across different regulations.
***
## Methods
### `list()`
```dart theme={null}
Future> list({int? page, int? perPage})
```
### `get()`
```dart theme={null}
Future get(String slug)
```
### `iter()`
```dart theme={null}
Stream iter({int perPage = 20})
```
***
## Model
### `Tag`
| Field | Type | Description |
| ------------- | -------------- | --------------------- |
| `id` | `int` | Internal ID |
| `slug` | `String` | URL-safe identifier |
| `name` | `String` | Display name |
| `description` | `String?` | Description |
| `keywords` | `List` | Associated keywords |
| `color` | `String?` | Hex color for display |
| `createdAt` | `String` | ISO 8601 timestamp |
***
## Examples
### List all tags
```dart theme={null}
final page = await client.tags.list();
for (final t in page.data) {
print('${t.name} — ${t.slug}');
}
```
### Get a specific tag
```dart theme={null}
final tag = await client.tags.get('data-protection');
print('Keywords: ${tag.keywords.join(', ')}');
```
### Auto-paginate
```dart theme={null}
await for (final t in client.tags.iter()) {
print(t.name);
}
```
# SDKs
Source: https://docs.law4devs.eu/sdks/index
Official Law4Devs SDKs for Python, TypeScript, PHP, Java, Flutter, and Rust
Choose Your SDK
Official client libraries for Python, TypeScript, PHP, Java, Flutter, and Rust — all with full feature parity.
`pip install law4devs` · Python 3.8+
`npm install law4devs` · Node 18+ / Deno / Bun
`composer require law4devs/law4devs` · PHP 8.1+
Maven · Java 17+ · Zero dependencies
`pub add law4devs` · Dart 3+ · Flutter & CLI
`cargo add law4devs` · Rust 1.85+ · Tokio async
## Feature Comparison
| Feature | Python | TypeScript | PHP | Java | Flutter | Rust |
| ----------------------------- | :-------: | :--------------: | :-: | :--------: | :-------------: | :-----------: |
| Auto-pagination with `iter()` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Full-text search | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Rate limit retry with backoff | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Async / non-blocking support | ✓ asyncio | ✓ Promise/async | — | — | ✓ Future/Stream | ✓ Tokio/async |
| Type safety | ✓ hints | ✓ interfaces | — | ✓ Records | ✓ null-safe | ✓ ownership |
| Stream-based iteration | — | ✓ AsyncGenerator | — | ✓ Iterable | ✓ Stream | ✓ Stream |
| Framework filtering | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Compliance deadline queries | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Mobile / web compatible | — | — | — | — | ✓ | — |
## Which SDK Should I Use?
You're building backend automation, data pipelines, or scripts. Best for compliance
engineers who want to process and analyse regulatory data programmatically.
You're building Node.js APIs, serverless functions, or browser-side tools. Best when
you need async operations and strong type safety.
You're working in a Laravel, Symfony, or WordPress application. The PHP SDK integrates
directly into Composer-based projects with no extra setup.
You're building JVM-based services, Spring Boot applications, or enterprise backend
systems. The Java SDK uses only stdlib — no external dependencies required.
You're building a mobile, desktop, or web Flutter app, or a Dart CLI tool. The only
SDK with full Flutter Web compatibility and Stream-based auto-pagination.
You're building high-performance backend services, CLI tools, or systems software where
memory safety and zero-cost abstractions matter. Best for latency-critical applications
that need async Tokio runtimes and compile-time type guarantees.
## Supported Frameworks
All 19 EU regulatory frameworks are available across every SDK — from GDPR and CRA to
the AI Act, DORA, NIS2, and beyond.
Full list of 19 EU regulations with CELEX numbers and status
# Annexes
Source: https://docs.law4devs.eu/sdks/java/annexes
List and retrieve annexes from EU regulatory frameworks with the Law4Devs Java SDK.
## Overview
Annexes contain supplementary material referenced by articles — including technical specifications, classification tables, and approved lists. Use `client.annexes()` to access them.
***
## Methods
### `list(String frameworkSlug)`
Returns the first page of annexes for a framework.
```java theme={null}
Page page = client.annexes().list("cra");
for (AnnexSummary a : page.data()) {
System.out.println("Annex " + a.number() + ": " + a.title());
}
```
**Returns** `Page`
***
### `list(String frameworkSlug, int page, int perPage)`
Fetch a specific page with custom page size.
```java theme={null}
Page page = client.annexes().list("cra", 1, 10);
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | -------- | ---------------------------- |
| `frameworkSlug` | `String` | required | Framework slug, e.g. `"cra"` |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
**Returns** `Page`
***
### `get()`
Fetch full detail for a single annex including all sections.
```java theme={null}
Annex annex = client.annexes().get("cra", "I");
System.out.println(annex.title()); // Essential cybersecurity requirements
for (var section : annex.sections()) {
System.out.println(section.reference() + ": " + section.text());
}
```
**Parameters**
| Parameter | Type | Description |
| --------------- | -------- | ------------------------------------ |
| `frameworkSlug` | `String` | Framework slug |
| `annexNumber` | `String` | Annex identifier, e.g. `"I"`, `"II"` |
**Returns** `Annex`
***
### `iter()`
Lazily iterate all annexes for a framework.
```java theme={null}
for (AnnexSummary a : client.annexes().iter("cra")) {
System.out.println("Annex " + a.number() + ": " + a.title());
}
```
**Returns** `Iterable`
***
## Models
### `AnnexSummary`
Returned by `list()` and `iter()`.
| Field | Type | Description |
| ----------------- | -------- | ---------------------------- |
| `id()` | `int` | Internal numeric ID |
| `number()` | `String` | Annex identifier, e.g. `"I"` |
| `title()` | `String` | Annex title |
| `frameworkSlug()` | `String` | Parent framework slug |
| `sectionCount()` | `int` | Number of sections |
### `Annex`
Returned by `get()`. Includes all `AnnexSummary` fields plus:
| Field | Type | Description |
| ------------ | -------------------- | -------------------------- |
| `sections()` | `List` | All sections of this annex |
### `AnnexSection`
| Field | Type | Description |
| ------------- | -------- | --------------------------------------- |
| `reference()` | `String` | Section reference, e.g. `"1"`, `"1(a)"` |
| `text()` | `String` | Full section text |
***
## Examples
### List all CRA annexes
```java theme={null}
for (AnnexSummary a : client.annexes().iter("cra")) {
System.out.printf("Annex %-4s %s (%d sections)%n",
a.number(), a.title(), a.sectionCount());
}
```
### Extract all security requirements from CRA Annex I
```java theme={null}
Annex annexI = client.annexes().get("cra", "I");
System.out.println("# " + annexI.title() + "\n");
for (var section : annexI.sections()) {
System.out.println(section.reference() + ". " + section.text());
}
```
# Articles
Source: https://docs.law4devs.eu/sdks/java/articles
List and retrieve articles from EU regulatory frameworks with the Law4Devs Java SDK.
## Overview
Articles are the numbered provisions within each regulation. Use `client.articles()` to list, fetch, and iterate articles for any framework.
***
## Methods
### `list(String frameworkSlug)`
Returns the first page of articles for a framework.
```java theme={null}
Page page = client.articles().list("gdpr");
for (ArticleSummary a : page.data()) {
System.out.println("Art. " + a.number() + " — " + a.title());
}
```
**Returns** `Page`
***
### `list(String frameworkSlug, int page, int perPage)`
Fetch a specific page with custom page size.
```java theme={null}
Page page = client.articles().list("cra", 1, 10);
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | -------- | ----------------------------- |
| `frameworkSlug` | `String` | required | Framework slug, e.g. `"gdpr"` |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
**Returns** `Page`
***
### `get()`
Fetch full detail for a single article including all paragraphs.
```java theme={null}
Article article = client.articles().get("gdpr", "17");
System.out.println(article.title()); // Right to erasure
System.out.println(article.number()); // 17
for (var p : article.paragraphs()) {
System.out.println(p.reference() + ": " + p.text());
}
```
**Parameters**
| Parameter | Type | Description |
| --------------- | -------- | -------------------------------------- |
| `frameworkSlug` | `String` | Framework slug |
| `articleNumber` | `String` | Article number, e.g. `"17"` or `"17a"` |
**Returns** `Article`
***
### `related()`
Fetch articles related to a given article (cross-references).
```java theme={null}
Page related = client.articles().related("gdpr", "17");
for (ArticleSummary a : related.data()) {
System.out.println("Related: Art. " + a.number() + " — " + a.title());
}
```
**Returns** `Page`
***
### `iter()`
Lazily iterate all articles for a framework.
```java theme={null}
for (ArticleSummary a : client.articles().iter("gdpr")) {
System.out.println(a.number() + " " + a.title());
}
```
**Returns** `Iterable`
***
## Models
### `ArticleSummary`
Returned by `list()`, `related()`, and `iter()`.
| Field | Type | Description |
| ------------------ | -------- | --------------------------- |
| `id()` | `int` | Internal numeric ID |
| `number()` | `String` | Article number, e.g. `"17"` |
| `title()` | `String` | Article title |
| `frameworkSlug()` | `String` | Parent framework slug |
| `paragraphCount()` | `int` | Number of paragraphs |
### `Article`
Returned by `get()`. Includes all `ArticleSummary` fields plus:
| Field | Type | Description |
| -------------- | ------------------------ | ----------------------------- |
| `paragraphs()` | `List` | All paragraphs of the article |
| `tags()` | `List` | Associated compliance tags |
### `ArticleParagraph`
| Field | Type | Description |
| ------------- | -------- | ----------------------------------------- |
| `reference()` | `String` | Paragraph reference, e.g. `"1"`, `"1(a)"` |
| `text()` | `String` | Full paragraph text |
***
## Examples
### Print all NIS2 articles
```java theme={null}
for (ArticleSummary a : client.articles().iter("nis2")) {
System.out.printf("Art. %-4s %s%n", a.number(), a.title());
}
```
### Find articles containing specific obligations
```java theme={null}
Article art = client.articles().get("gdpr", "33");
System.out.println(art.title()); // Notification of a personal data breach
for (var p : art.paragraphs()) {
if (p.text().contains("72 hours")) {
System.out.println("72-hour rule in: " + p.reference());
}
}
```
### Collect all DORA articles into a list
```java theme={null}
var all = new java.util.ArrayList();
for (ArticleSummary a : client.articles().iter("dora")) {
all.add(a);
}
System.out.println("Total DORA articles: " + all.size());
```
# Compliance Deadlines
Source: https://docs.law4devs.eu/sdks/java/compliance
Query compliance deadlines and enforcement dates with the Law4Devs Java SDK.
## Overview
Compliance deadlines track the key enforcement dates, transition periods, and application dates for each regulation. Use `client.compliance()` to access them.
***
## Methods
### `deadlines()`
Returns the first page of compliance deadlines across all frameworks.
```java theme={null}
Page page = client.compliance().deadlines();
for (ComplianceDeadline d : page.data()) {
System.out.printf("[%s] %s — %s%n",
d.frameworkSlug(), d.date(), d.description());
}
```
**Returns** `Page`
***
### `deadlines(String frameworkSlug)`
Returns the first page of deadlines scoped to one framework.
```java theme={null}
Page page = client.compliance().deadlines("nis2");
```
**Returns** `Page`
***
### `deadlines(String frameworkSlug, int page, int perPage)`
Fetch a specific page with custom page size.
```java theme={null}
Page page = client.compliance().deadlines("dora", 1, 50);
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | ------- | --------------------------------- |
| `frameworkSlug` | `String` | `null` | Scope to one framework (optional) |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
**Returns** `Page`
***
### `iterDeadlines()`
Lazily iterate all compliance deadlines across all frameworks.
```java theme={null}
for (ComplianceDeadline d : client.compliance().iterDeadlines()) {
System.out.println(d.frameworkSlug() + " — " + d.date() + ": " + d.description());
}
```
**Returns** `Iterable`
***
### `iterDeadlines(String frameworkSlug)`
Lazily iterate all deadlines for a specific framework.
```java theme={null}
for (ComplianceDeadline d : client.compliance().iterDeadlines("cra")) {
System.out.println(d.date() + ": " + d.description());
}
```
**Returns** `Iterable`
***
## Model
### `ComplianceDeadline`
| Field | Type | Description |
| -------------------- | -------- | ----------------------------------------------------- |
| `id()` | `int` | Internal numeric ID |
| `frameworkSlug()` | `String` | Parent framework slug |
| `frameworkName()` | `String` | Human-readable framework name |
| `date()` | `String` | ISO 8601 deadline date |
| `description()` | `String` | What must be complied with by this date |
| `deadlineType()` | `String` | e.g. `"enforcement"`, `"transition"`, `"application"` |
| `articleReference()` | `String` | Source article or recital reference |
***
## Examples
### Print upcoming CRA deadlines in order
```java theme={null}
var deadlines = new java.util.ArrayList();
for (ComplianceDeadline d : client.compliance().iterDeadlines("cra")) {
deadlines.add(d);
}
deadlines.sort(java.util.Comparator.comparing(ComplianceDeadline::date));
for (ComplianceDeadline d : deadlines) {
System.out.printf("%s %s%n", d.date(), d.description());
}
```
### List all enforcement deadlines across frameworks
```java theme={null}
for (ComplianceDeadline d : client.compliance().iterDeadlines()) {
if ("enforcement".equals(d.deadlineType())) {
System.out.printf("[%-6s] %s — %s%n",
d.frameworkSlug(), d.date(), d.description());
}
}
```
### Build a timeline report
```java theme={null}
var byFramework = new java.util.TreeMap>();
for (ComplianceDeadline d : client.compliance().iterDeadlines()) {
byFramework.computeIfAbsent(d.frameworkSlug(),
k -> new java.util.ArrayList<>()).add(d);
}
byFramework.forEach((fw, list) -> {
System.out.println("\n## " + fw.toUpperCase());
list.forEach(d -> System.out.println(" " + d.date() + " — " + d.description()));
});
```
# Error Handling
Source: https://docs.law4devs.eu/sdks/java/error-handling
Handle API errors gracefully with the Law4Devs Java SDK.
## Exception Hierarchy
All SDK exceptions extend `eu.law4devs.sdk.exceptions.Law4DevsException`, which itself extends `RuntimeException`.
```
RuntimeException
└── Law4DevsException base — any SDK error
├── NotFoundError 404 — resource not found
├── ValidationError 400 — invalid parameters
├── RateLimitError 429 — rate limit exceeded
└── ServerError 5xx — server-side error
```
Every exception exposes a `getStatusCode()` method:
```java theme={null}
try {
Article article = client.articles().get("gdpr", 9999);
} catch (NotFoundError e) {
System.out.println("Not found: " + e.getMessage()); // message from API
System.out.println("Status: " + e.getStatusCode()); // 404
}
```
***
## Catching Specific Errors
```java theme={null}
import eu.law4devs.sdk.exceptions.Law4DevsException;
import eu.law4devs.sdk.exceptions.NotFoundError;
import eu.law4devs.sdk.exceptions.RateLimitError;
import eu.law4devs.sdk.exceptions.ValidationError;
try {
var page = client.articles().list("invalid-framework");
} catch (NotFoundError e) {
// Framework not found
System.out.println("Framework not found: " + e.getMessage());
} catch (ValidationError e) {
// Bad request parameters
System.out.println("Invalid request: " + e.getMessage());
} catch (RateLimitError e) {
// Retry after a delay (SDK already retried 3 times)
System.out.println("Rate limit hit: " + e.getMessage());
} catch (Law4DevsException e) {
// All other SDK errors (server errors, network failures)
System.out.println("SDK error (" + e.getStatusCode() + "): " + e.getMessage());
}
```
***
## Automatic Retry
The SDK automatically retries on `429 Too Many Requests` and `5xx Server Error` responses using exponential backoff:
| Attempt | Wait |
| --------- | --------- |
| 1st retry | 1 second |
| 2nd retry | 2 seconds |
| 3rd retry | 4 seconds |
After `maxRetries` (default `3`) attempts, the exception is re-thrown.
To disable retries, set `maxRetries(0)`:
```java theme={null}
Law4DevsClient client = Law4DevsClient.builder()
.apiKey(System.getenv("LAW4DEVS_API_KEY"))
.maxRetries(0)
.build();
```
`NotFoundError` (404) and `ValidationError` (400) are never retried — they represent client errors that will not resolve by retrying.
***
## Network Errors
Connection failures (timeout, DNS, SSL) throw `Law4DevsException` with a message describing the failure:
```java theme={null}
import eu.law4devs.sdk.exceptions.Law4DevsException;
try {
var page = client.frameworks().list();
} catch (Law4DevsException e) {
System.out.println("SDK error: " + e.getMessage());
}
```
***
## Errors in `iter()` Loops
Exceptions can be thrown mid-iteration if a page request fails. Wrap `iter()` loops in a try/catch:
```java theme={null}
try {
for (ArticleSummary a : client.articles().iter("gdpr")) {
System.out.println(a.title());
}
} catch (Law4DevsException e) {
System.out.println("Error mid-iteration: " + e.getMessage());
}
```
***
## Best Practices
* Always catch `Law4DevsException` as a fallback to handle unexpected errors
* Wrap `iter()` loops in a try/catch — exceptions can occur mid-iteration if a page request fails
* Use `getStatusCode()` to distinguish error types when handling `Law4DevsException` generically
* For batch processing, implement a retry strategy at the application level if you need finer control than the built-in retry
* Prefer specific exception types (`NotFoundError`, `RateLimitError`) over the base class for cleaner error handling
# Frameworks
Source: https://docs.law4devs.eu/sdks/java/frameworks
List and retrieve EU regulatory frameworks with the Law4Devs Java SDK.
## Overview
Frameworks represent EU regulations such as GDPR, CRA, NIS2, and the AI Act. Use `client.frameworks()` to list and fetch them.
***
## Methods
### `list()`
Returns a page of all available frameworks.
```java theme={null}
Page page = client.frameworks().list();
System.out.printf("Found %d frameworks%n", page.meta().total());
for (Framework fw : page.data()) {
System.out.printf(" %-12s %s%n", fw.slug(), fw.name());
}
```
**Returns** `Page`
***
### `list(int page, int perPage)`
Fetch a specific page with custom page size.
```java theme={null}
Page page = client.frameworks().list(1, 5);
```
**Parameters**
| Parameter | Type | Default | Description |
| --------- | ----- | ------- | ------------------------ |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
**Returns** `Page`
***
### `get()`
Fetch full detail for a single framework by slug.
```java theme={null}
FrameworkDetail cra = client.frameworks().get("cra");
System.out.println(cra.name()); // Cyber Resilience Act
System.out.println(cra.celexNumber()); // 32024R2847
System.out.println(cra.articleCount()); // number of synced articles
```
**Parameters**
| Parameter | Type | Description |
| --------- | -------- | ----------------------------- |
| `slug` | `String` | Framework slug, e.g. `"gdpr"` |
**Returns** `FrameworkDetail`
***
### `iter()`
Lazily iterate all frameworks across all pages.
```java theme={null}
for (Framework fw : client.frameworks().iter()) {
System.out.println(fw.slug() + ": " + fw.name());
}
```
**Returns** `Iterable`
***
## Models
### `Framework`
Returned by `list()` and `iter()`.
| Field | Type | Description |
| ---------------- | --------- | ---------------------------------------- |
| `id()` | `int` | Internal numeric ID |
| `slug()` | `String` | URL-safe identifier, e.g. `"cra"` |
| `name()` | `String` | Full framework name |
| `shortName()` | `String` | Short name, e.g. `"CRA"` |
| `celexNumber()` | `String` | EUR-Lex CELEX identifier |
| `description()` | `String` | Brief description |
| `isActive()` | `boolean` | Whether data is actively maintained |
| `status()` | `String` | `"active"`, `"draft"`, or `"superseded"` |
| `articleCount()` | `int` | Number of synced articles |
| `recitalCount()` | `int` | Number of synced recitals |
| `createdAt()` | `String` | ISO 8601 creation timestamp |
### `FrameworkDetail`
Returned by `get()`. Includes all `Framework` fields plus:
| Field | Type | Description |
| -------------------- | -------- | --------------------------------- |
| `eurLexUrl()` | `String` | Link to the official EUR-Lex page |
| `requirementCount()` | `int` | Number of extracted requirements |
| `annexCount()` | `int` | Number of annexes |
| `tagCount()` | `int` | Number of associated tags |
***
## Examples
### List all active frameworks
```java theme={null}
for (Framework fw : client.frameworks().iter()) {
if (fw.isActive()) {
System.out.printf("%-12s %s (%d articles)%n",
fw.slug(), fw.name(), fw.articleCount());
}
}
```
### Build a framework lookup map
```java theme={null}
var map = new java.util.LinkedHashMap();
for (Framework fw : client.frameworks().iter()) {
map.put(fw.slug(), fw);
}
Framework gdpr = map.get("gdpr");
System.out.println(gdpr.name()); // General Data Protection Regulation
```
### Print frameworks with full article coverage
```java theme={null}
var page = client.frameworks().list();
for (Framework fw : page.data()) {
if (fw.expectedArticles() > 0 && fw.articleCount() >= fw.expectedArticles()) {
System.out.println(fw.name() + " — fully synced");
}
}
```
# Installation
Source: https://docs.law4devs.eu/sdks/java/installation
Install the Law4Devs Java SDK and make your first API call.
## Requirements
* Java 17 or later
* Maven 3.6+ (build tool)
* No runtime dependencies — uses Java standard library only
## Install
Add to your `pom.xml`:
```xml theme={null}
eu.law4devs.sdk
java
1.0.0
```
Then run:
```bash theme={null}
mvn install
```
To pin to a specific version:
```xml theme={null}
eu.law4devs.sdk
java
1.0.0
```
## First Call
```java theme={null}
import eu.law4devs.sdk.Law4DevsClient;
import eu.law4devs.sdk.models.Framework;
import eu.law4devs.sdk.pagination.Page;
Law4DevsClient client = Law4DevsClient.builder()
.apiKey(System.getenv("LAW4DEVS_API_KEY"))
.build();
Page page = client.frameworks().list();
System.out.println(page.data().get(0).name()); // e.g. "Cyber Resilience Act"
```
## Configuration
```java theme={null}
import eu.law4devs.sdk.Law4DevsClient;
import java.time.Duration;
Law4DevsClient client = Law4DevsClient.builder()
.apiKey(System.getenv("LAW4DEVS_API_KEY"))
.baseUrl("https://api.law4devs.eu/v1") // default
.timeout(Duration.ofSeconds(30)) // default 30s
.maxRetries(3) // retries on 429/5xx, default 3
.build();
```
| Option | Type | Default | Description |
| ------------ | ---------- | ------------------------ | --------------------------------------------------- |
| `apiKey` | `String` | `null` | Your API key (required for authenticated endpoints) |
| `baseUrl` | `String` | Production URL | Override for dev/staging environment |
| `timeout` | `Duration` | `Duration.ofSeconds(30)` | Per-request HTTP timeout |
| `maxRetries` | `int` | `3` | Retries on 429/5xx responses |
## Environment Variable
```bash theme={null}
export LAW4DEVS_API_KEY="your-key"
```
```java theme={null}
Law4DevsClient client = Law4DevsClient.builder()
.apiKey(System.getenv("LAW4DEVS_API_KEY"))
.build();
```
Never hardcode your API key in source code or commit it to version control.
## Verify the Install
```java theme={null}
// VerifyInstall.java
import eu.law4devs.sdk.Law4DevsClient;
public class VerifyInstall {
public static void main(String[] args) {
var client = Law4DevsClient.builder()
.apiKey(System.getenv("LAW4DEVS_API_KEY"))
.build();
System.out.println(client.frameworks().list().meta().total() + " frameworks");
}
}
```
```bash theme={null}
LAW4DEVS_API_KEY=your-key mvn compile exec:java -Dexec.mainClass="VerifyInstall"
# → 19 frameworks
```
# Pagination
Source: https://docs.law4devs.eu/sdks/java/pagination
How pagination works in the Law4Devs Java SDK.
## Overview
All `list()` methods return a `Page` object. Every page includes the data list, metadata about the total result set, and links to the next/previous page.
***
## The `Page` Object
```java theme={null}
Page page = client.articles().list("gdpr");
// Data
List articles = page.data(); // items on this page
int count = page.data().size(); // number of items returned
// Metadata
int total = page.meta().total(); // total results across all pages
int current = page.meta().page(); // current page number
int perPage = page.meta().perPage(); // items per page
int pages = page.meta().pages(); // total number of pages
// Links
String nextUrl = page.links().next(); // URL of next page (null if last)
String prevUrl = page.links().prev(); // URL of previous page (null if first)
```
All model fields in Java records are accessed via method calls (e.g. `page.meta().total()`, not `page.meta().total`).
***
## Manual Pagination
Control pagination yourself with `page` and `perPage` parameters:
```java theme={null}
int pageNum = 1;
Page page;
do {
page = client.articles().list("gdpr", pageNum, 50);
for (ArticleSummary a : page.data()) {
System.out.println(a.title());
}
pageNum++;
} while (page.links().next() != null);
```
***
## Auto-Pagination with `iter()`
Every resource exposes an `iter()` method that automatically fetches all pages and yields individual items:
```java theme={null}
// Yields ArticleSummary one by one — no manual paging needed
for (ArticleSummary a : client.articles().iter("gdpr")) {
System.out.println(a.number() + " " + a.title());
}
```
Internally, `iter()` uses `PageIterator`, which calls `list()` repeatedly and follows `links().next()` until there are no more pages.
Use `iter()` whenever you need all items. It handles pagination transparently without loading the full result set into memory at once — each page is fetched only when the previous one is exhausted.
***
## Collecting into a List
If you need all results in a `List`, iterate and collect:
```java theme={null}
var all = new java.util.ArrayList();
for (ArticleSummary a : client.articles().iter("cra")) {
all.add(a);
}
System.out.println(all.size() + " total articles");
```
Or with streams:
```java theme={null}
import java.util.stream.StreamSupport;
var all = StreamSupport
.stream(client.articles().iter("cra").spliterator(), false)
.toList();
System.out.println(all.size() + " total articles");
```
***
## Early Stopping
Break out of an `iter()` loop at any point — no extra pages are fetched after the break:
```java theme={null}
for (ArticleSummary a : client.articles().iter("gdpr")) {
if (a.number().equals("50")) {
System.out.println("Found Art. 50: " + a.title());
break;
}
}
```
***
## Progress Reporting
```java theme={null}
Page first = client.articles().list("gdpr", 1, 20);
int total = first.meta().total();
int processed = 0;
for (ArticleSummary a : client.articles().iter("gdpr")) {
processed++;
System.out.printf("\r%d / %d", processed, total);
}
System.out.println();
```
***
## Pagination Parameters
| Parameter | Type | Default | Description |
| --------- | ----- | ------- | ------------------------ |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
# Quick Start
Source: https://docs.law4devs.eu/sdks/java/quickstart
Make your first Law4Devs API call with Java in under 60 seconds.
## Install
Add to your `pom.xml`:
```xml theme={null}
eu.law4devs.sdk
java
1.0.0
```
## Basic Usage
```java theme={null}
import eu.law4devs.sdk.Law4DevsClient;
import eu.law4devs.sdk.models.ArticleSummary;
import eu.law4devs.sdk.models.Framework;
var client = Law4DevsClient.builder()
.apiKey(System.getenv("LAW4DEVS_API_KEY"))
.build();
// List all frameworks
var page = client.frameworks().list();
for (Framework fw : page.data()) {
System.out.println(fw.slug() + " — " + fw.name());
}
// List first 5 CRA articles
var articles = client.articles().list("cra", 1, 5);
for (ArticleSummary a : articles.data()) {
System.out.println("Art. " + a.number() + ": " + a.title());
}
// Auto-paginate all GDPR articles
for (ArticleSummary a : client.articles().iter("gdpr")) {
System.out.println(a.number() + " " + a.title());
}
// Search across all frameworks
var results = client.search().query("data breach notification");
System.out.println("Found " + results.meta().total() + " results");
```
## Next Steps
# Recitals
Source: https://docs.law4devs.eu/sdks/java/recitals
List and retrieve recitals from EU regulatory frameworks with the Law4Devs Java SDK.
## Overview
Recitals are the numbered preamble paragraphs that provide context and legislative intent for each regulation. Use `client.recitals()` to access them.
***
## Methods
### `list(String frameworkSlug)`
Returns the first page of recitals for a framework.
```java theme={null}
Page page = client.recitals().list("gdpr");
for (Recital r : page.data()) {
System.out.println("Recital " + r.number() + ": " + r.title());
}
```
**Returns** `Page`
***
### `list(String frameworkSlug, int page, int perPage)`
Fetch a specific page with custom page size.
```java theme={null}
Page page = client.recitals().list("cra", 1, 50);
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | -------- | ----------------------------- |
| `frameworkSlug` | `String` | required | Framework slug, e.g. `"gdpr"` |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
**Returns** `Page`
***
### `get()`
Fetch a single recital by number.
```java theme={null}
Recital r = client.recitals().get("gdpr", "4");
System.out.println(r.title()); // Right to the protection of personal data
System.out.println(r.text()); // Full recital text
```
**Parameters**
| Parameter | Type | Description |
| --------------- | -------- | -------------------------- |
| `frameworkSlug` | `String` | Framework slug |
| `recitalNumber` | `String` | Recital number, e.g. `"4"` |
**Returns** `Recital`
***
### `iter()`
Lazily iterate all recitals for a framework.
```java theme={null}
for (Recital r : client.recitals().iter("nis2")) {
System.out.println("Recital " + r.number() + ": " + r.title());
}
```
**Returns** `Iterable`
***
## Model
### `Recital`
| Field | Type | Description |
| ----------------- | -------- | -------------------------- |
| `id()` | `int` | Internal numeric ID |
| `number()` | `String` | Recital number, e.g. `"4"` |
| `title()` | `String` | Short title or topic |
| `text()` | `String` | Full recital text |
| `frameworkSlug()` | `String` | Parent framework slug |
***
## Examples
### Search recitals for a keyword
```java theme={null}
for (Recital r : client.recitals().iter("gdpr")) {
if (r.text().contains("consent")) {
System.out.println("Recital " + r.number() + ": " + r.title());
}
}
```
### Build a context document for legal review
```java theme={null}
var sb = new StringBuilder();
sb.append("# CRA Recitals\n\n");
for (Recital r : client.recitals().iter("cra")) {
sb.append("## Recital ").append(r.number()).append("\n");
sb.append(r.text()).append("\n\n");
}
System.out.println(sb);
```
# Requirements
Source: https://docs.law4devs.eu/sdks/java/requirements
Query extracted compliance requirements with the Law4Devs Java SDK.
## Overview
Requirements are structured, machine-readable compliance obligations extracted from articles. They are classified by type and stakeholder role. Use `client.requirements()` to list and iterate them.
***
## Methods
### `list()`
Returns the first page of requirements across all frameworks.
```java theme={null}
Page page = client.requirements().list();
for (Requirement r : page.data()) {
System.out.printf("[%s] %s — %s%n",
r.frameworkSlug(), r.requirementType(), r.description());
}
```
**Returns** `Page`
***
### `list(String frameworkSlug)`
Returns the first page of requirements scoped to a specific framework.
```java theme={null}
Page page = client.requirements().list("cra");
```
**Returns** `Page`
***
### `list(String frameworkSlug, int page, int perPage)`
Fetch a specific page with custom page size.
```java theme={null}
Page page = client.requirements().list("nis2", 1, 50);
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | ------- | --------------------------------- |
| `frameworkSlug` | `String` | `null` | Scope to one framework (optional) |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
**Returns** `Page`
***
### `iter()`
Lazily iterate all requirements across all frameworks.
```java theme={null}
for (Requirement r : client.requirements().iter()) {
System.out.println(r.description());
}
```
**Returns** `Iterable`
***
### `iter(String frameworkSlug)`
Lazily iterate all requirements for a specific framework.
```java theme={null}
for (Requirement r : client.requirements().iter("dora")) {
System.out.printf(" [%s] %s%n", r.requirementType(), r.description());
}
```
**Returns** `Iterable`
***
## Model
### `Requirement`
| Field | Type | Description |
| ------------------- | -------- | -------------------------------------------------- |
| `id()` | `int` | Internal numeric ID |
| `frameworkSlug()` | `String` | Parent framework slug |
| `articleNumber()` | `String` | Source article number |
| `requirementType()` | `String` | e.g. `"MUST"`, `"SHOULD"`, `"MAY"` |
| `description()` | `String` | Human-readable requirement description |
| `stakeholderRole()` | `String` | Target role, e.g. `"manufacturer"`, `"controller"` |
| `legalReference()` | `String` | Article and paragraph reference |
***
## Examples
### List MUST requirements for CRA manufacturers
```java theme={null}
for (Requirement r : client.requirements().iter("cra")) {
if ("MUST".equals(r.requirementType()) &&
"manufacturer".equals(r.stakeholderRole())) {
System.out.println("Art. " + r.articleNumber() + ": " + r.description());
}
}
```
### Count requirements by framework
```java theme={null}
var counts = new java.util.LinkedHashMap();
for (Requirement r : client.requirements().iter()) {
counts.merge(r.frameworkSlug(), 1, Integer::sum);
}
counts.forEach((fw, n) -> System.out.printf("%-12s %d requirements%n", fw, n));
```
### Build a compliance checklist
```java theme={null}
var checklist = new java.util.ArrayList();
for (Requirement r : client.requirements().iter("gdpr")) {
if ("controller".equals(r.stakeholderRole())) {
checklist.add("[ ] " + r.description());
}
}
checklist.forEach(System.out::println);
```
# Search
Source: https://docs.law4devs.eu/sdks/java/search
Full-text search across all EU frameworks with the Law4Devs Java SDK.
## Overview
The search endpoint lets you query across all frameworks simultaneously. Results include articles, recitals, and requirements that match your query. Use `client.search().query()` to search.
***
## Methods
### `query(String q)`
Search across all frameworks with default pagination.
```java theme={null}
Page results = client.search().query("incident notification 72 hours");
System.out.printf("Found %d results%n", results.meta().total());
for (SearchResult r : results.data()) {
System.out.printf(" [%s] %s — %s%n",
r.frameworkSlug().toUpperCase(),
r.type(),
r.title() != null ? r.title() : r.matchContext());
}
```
**Returns** `Page`
***
### `query(String q, String framework, String resultType, int page, int perPage)`
Search with optional framework scope, result type filter, and custom pagination.
```java theme={null}
Page results = client.search().query(
"breach notification", // query
"gdpr", // framework filter (null = all)
"article", // result type filter (null = all)
1, // page
20 // perPage
);
```
**Parameters**
| Parameter | Type | Default | Description |
| ------------ | -------- | -------- | --------------------------------------------------------- |
| `q` | `String` | required | Search query |
| `framework` | `String` | `null` | Limit to one framework slug |
| `resultType` | `String` | `null` | Filter by type: `"article"`, `"recital"`, `"requirement"` |
| `page` | `int` | `1` | Page number |
| `perPage` | `int` | `20` | Items per page |
**Returns** `Page`
***
### `iter(String q)`
Lazily iterate all search results for a query.
```java theme={null}
for (SearchResult r : client.search().iter("cyber resilience")) {
System.out.printf("[%s] %s%n", r.frameworkSlug(), r.matchContext());
}
```
**Returns** `Iterable`
***
### `iter(String q, String framework, String resultType)`
Lazily iterate with optional framework and result-type filters.
```java theme={null}
for (SearchResult r : client.search().iter("encryption", "cra", "requirement")) {
System.out.println(r.matchContext());
}
```
**Returns** `Iterable`
***
## Model
### `SearchResult`
| Field | Type | Description |
| ------------------- | -------- | -------------------------------------------- |
| `type()` | `String` | `"article"`, `"recital"`, or `"requirement"` |
| `frameworkSlug()` | `String` | Framework the result belongs to |
| `frameworkName()` | `String` | Human-readable framework name |
| `articleNumber()` | `String` | For article results |
| `recitalNumber()` | `String` | For recital results |
| `paragraphRef()` | `String` | Specific paragraph reference |
| `title()` | `String` | Result heading, if any |
| `requirementType()` | `String` | For requirement results |
| `matchContext()` | `String` | Snippet showing the match in context |
| `url()` | `String` | Direct API URL to the full resource |
***
## Examples
### Search within a specific framework
```java theme={null}
Page results = client.search().query(
"breach notification", "gdpr", null, 1, 20);
for (SearchResult r : results.data()) {
System.out.println(r.frameworkSlug() + " — " +
(r.title() != null ? r.title() : r.matchContext()));
}
```
### Filter by result type
```java theme={null}
Page results = client.search().query(
"encryption at rest", null, "requirement", 1, 20);
for (SearchResult r : results.data()) {
System.out.println("- " + r.matchContext());
}
```
### Cross-framework comparison
```java theme={null}
String[] frameworks = {"nis2", "dora", "cra"};
for (String fw : frameworks) {
Page r = client.search().query(
"incident notification", fw, null, 1, 1);
System.out.printf("%-6s %d results%n", fw, r.meta().total());
}
```
Use the `framework` parameter to narrow results when you know which regulation you're working with. Cross-framework search (without a filter) is great for discovering related obligations across different laws.
# Tags
Source: https://docs.law4devs.eu/sdks/java/tags
List and retrieve compliance tags with the Law4Devs Java SDK.
## Overview
Tags are semantic labels that categorise articles and requirements by topic (e.g. `data-breach`, `encryption`, `audit`). Use `client.tags()` to list and fetch them.
***
## Methods
### `list()`
Returns the first page of all available tags.
```java theme={null}
Page page = client.tags().list();
for (Tag t : page.data()) {
System.out.println(t.slug() + " — " + t.name());
}
```
**Returns** `Page`
***
### `list(int page, int perPage)`
Fetch a specific page with custom page size.
```java theme={null}
Page page = client.tags().list(1, 50);
```
**Parameters**
| Parameter | Type | Default | Description |
| --------- | ----- | ------- | ------------------------ |
| `page` | `int` | `1` | Page number (1-indexed) |
| `perPage` | `int` | `20` | Items per page (max 100) |
**Returns** `Page`
***
### `get()`
Fetch a single tag by slug.
```java theme={null}
Tag tag = client.tags().get("data-breach");
System.out.println(tag.name()); // Data Breach
System.out.println(tag.description()); // Requirements related to data breach...
```
**Parameters**
| Parameter | Type | Description |
| --------- | -------- | ------------------------------ |
| `slug` | `String` | Tag slug, e.g. `"data-breach"` |
**Returns** `Tag`
***
### `iter()`
Lazily iterate all tags.
```java theme={null}
for (Tag t : client.tags().iter()) {
System.out.println(t.slug() + ": " + t.name());
}
```
**Returns** `Iterable`
***
## Model
### `Tag`
| Field | Type | Description |
| ---------------- | -------- | ----------------------------------------- |
| `id()` | `int` | Internal numeric ID |
| `slug()` | `String` | URL-safe identifier, e.g. `"data-breach"` |
| `name()` | `String` | Human-readable tag name |
| `description()` | `String` | What this tag covers |
| `articleCount()` | `int` | Number of tagged articles |
***
## Examples
### Print all available tags
```java theme={null}
for (Tag t : client.tags().iter()) {
System.out.printf("%-25s %d articles%n", t.name(), t.articleCount());
}
```
### Build a tag index
```java theme={null}
var index = new java.util.LinkedHashMap();
for (Tag t : client.tags().iter()) {
index.put(t.slug(), t);
}
Tag breach = index.get("data-breach");
System.out.println(breach.name() + " covers " + breach.articleCount() + " articles");
```
# Annexes
Source: https://docs.law4devs.eu/sdks/php/annexes
Fetch framework annexes with the Law4Devs PHP SDK.
## Overview
Annexes are supplementary parts of EU regulations that appear after the main articles. They often contain technical requirements, lists, or tables referenced in the body. Use `$client->annexes` to access them.
***
## Methods
### `list()`
Returns a page of annex summaries for a framework.
```php theme={null}
$page = $client->annexes->list('cra');
foreach ($page->data as $annex) {
printf(" Annex %s: %s\n", $annex->annexNumber, $annex->title);
}
```
**Returns** `Page`
***
### `get()`
Fetch the full content of a single annex.
```php theme={null}
$annex = $client->annexes->get('cra', 1);
echo $annex->title . "\n";
echo $annex->content . "\n";
```
**Returns** `Annex`
***
### `iter()`
Iterate over all annex summaries in a framework.
```php theme={null}
foreach ($client->annexes->iter('cra') as $annex) {
echo $annex->annexNumber . ': ' . $annex->title . "\n";
}
```
**Returns** `\Generator`
***
## Models
### `AnnexSummary`
| Field | Type | Description |
| --------------- | -------- | ------------------------------------ |
| `id` | `int` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `annexNumber` | `string` | Annex identifier, e.g. `'I'`, `'II'` |
| `title` | `string` | Annex heading |
### `Annex`
Returned by `get()`. Extends `AnnexSummary` with:
| Field | Type | Description |
| --------- | -------- | ------------------------------ |
| `content` | `string` | Full text content of the annex |
# Articles
Source: https://docs.law4devs.eu/sdks/php/articles
Fetch and iterate framework articles with the Law4Devs PHP SDK.
## Overview
Articles are the primary legal content of each framework. Every article belongs to exactly one framework, identified by its slug. Use `$client->articles` to list, fetch, and navigate them.
***
## Methods
### `list()`
Returns a page of article summaries for the given framework.
```php theme={null}
$page = $client->articles->list('gdpr', page: 1, perPage: 20);
printf("GDPR has %d articles\n", $page->meta->total);
foreach ($page->data as $article) {
printf(" Art. %s: %s\n", $article->articleNumber, $article->title);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------------- | ----------- | -------- | ----------------------------- |
| `$frameworkSlug` | `string` | required | Framework slug, e.g. `'gdpr'` |
| `$page` | `int\|null` | `null` | Page number |
| `$perPage` | `int\|null` | `null` | Items per page (1–100) |
**Returns** `Page`
***
### `get()`
Fetch the full content of a single article, including all paragraphs.
```php theme={null}
$article = $client->articles->get('gdpr', 17);
echo $article->title . "\n";
echo $article->content . "\n";
foreach ($article->paragraphs as $para) {
printf(" §%s: %s\n", $para->paragraphRef, $para->content);
}
```
**Parameters**
| Parameter | Type | Description |
| ---------------- | ------------- | ----------------------------------- |
| `$frameworkSlug` | `string` | Framework slug |
| `$articleNumber` | `int\|string` | Article number within the framework |
**Returns** `Article`
***
### `related()`
Fetch articles related to a given article.
```php theme={null}
$page = $client->articles->related('gdpr', 17);
foreach ($page->data as $article) {
printf(" Related: Art. %s — %s\n", $article->articleNumber, $article->title);
}
```
**Returns** `Page`
***
### `iter()`
Iterate over all articles in a framework across all pages.
```php theme={null}
foreach ($client->articles->iter('gdpr') as $article) {
printf("Art. %s: %s\n", $article->articleNumber, $article->title);
}
```
**Returns** `\Generator`
***
## Models
### `ArticleSummary`
Returned by `list()`, `related()`, and `iter()`.
| Field | Type | Description |
| ---------------- | -------- | ----------------------------------- |
| `id` | `int` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `articleNumber` | `string` | Article number within the framework |
| `title` | `string` | Article heading |
| `position` | `int` | Ordering position in the framework |
| `paragraphCount` | `int` | Number of paragraphs |
| `tags` | `Tag[]` | Tags applied to this article |
### `Article`
Returned by `get()`. Extends `ArticleSummary` with:
| Field | Type | Description |
| ------------ | -------------------- | -------------------------------------- |
| `content` | `string` | Full plain-text content of the article |
| `paragraphs` | `ArticleParagraph[]` | Ordered list of paragraph objects |
### `ArticleParagraph`
| Field | Type | Description |
| -------------- | -------- | ------------------------------------ |
| `paragraphRef` | `string` | Label, e.g. `'1'`, `'2a'`, `'intro'` |
| `content` | `string` | Paragraph text |
| `position` | `int` | Ordering position within the article |
***
## Examples
### Print every GDPR article
```php theme={null}
foreach ($client->articles->iter('gdpr') as $article) {
printf("Art. %3s %s\n", $article->articleNumber, $article->title ?: '(untitled)');
}
```
### Fetch a specific article and display paragraphs
```php theme={null}
$article = $client->articles->get('gdpr', 17);
echo "Article {$article->articleNumber}: {$article->title}\n";
echo str_repeat('=', 60) . "\n";
foreach ($article->paragraphs as $para) {
echo "\n({$para->paragraphRef}) {$para->content}\n";
}
```
### Collect all articles into an array for fast lookup
```php theme={null}
$byNumber = [];
foreach ($client->articles->iter('cra') as $article) {
$byNumber[$article->articleNumber] = $article;
}
echo $byNumber['10']->title . "\n";
```
Use `iter()` when you need every article. Use `list()` when you only need one page or want to control pagination yourself.
# Compliance Deadlines
Source: https://docs.law4devs.eu/sdks/php/compliance
Fetch compliance deadlines for EU frameworks with the Law4Devs PHP SDK.
## Overview
Compliance deadlines are structured dates extracted from regulatory texts, indicating when specific obligations must be met. Use `$client->compliance` to access them.
***
## Methods
### `list()`
Returns a page of compliance deadlines for a framework.
```php theme={null}
$page = $client->compliance->list('nis2');
foreach ($page->data as $deadline) {
printf(" [%s] %s — %s\n",
$deadline->deadlineDate,
$deadline->deadlineType,
$deadline->description ?? 'No description',
);
}
```
**Returns** `Page`
***
### `iter()`
Iterate over all compliance deadlines in a framework.
```php theme={null}
foreach ($client->compliance->iter('dora') as $deadline) {
echo $deadline->deadlineDate . ': ' . $deadline->description . "\n";
}
```
**Returns** `\Generator`
***
## Model
### `ComplianceDeadline`
| Field | Type | Description |
| --------------- | -------------- | --------------------------------------------- |
| `id` | `int` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `articleNumber` | `string\|null` | Source article number |
| `paragraphRef` | `string\|null` | Paragraph reference |
| `deadlineDate` | `string` | ISO 8601 date |
| `deadlineType` | `string` | Type, e.g. `'transposition'`, `'application'` |
| `description` | `string\|null` | Human-readable description of the obligation |
***
## Example
### Print all upcoming deadlines sorted by date
```php theme={null}
$deadlines = [];
foreach ($client->compliance->iter('nis2') as $d) {
$deadlines[] = $d;
}
usort($deadlines, fn($a, $b) => $a->deadlineDate <=> $b->deadlineDate);
foreach ($deadlines as $d) {
printf("[%s] %s\n", $d->deadlineDate, $d->description ?? $d->deadlineType);
}
```
# Error Handling
Source: https://docs.law4devs.eu/sdks/php/error-handling
Handle API errors gracefully with the Law4Devs PHP SDK.
## Exception Hierarchy
All SDK exceptions extend `Law4Devs\Exceptions\Law4DevsException`, which itself extends `\RuntimeException`.
```
\RuntimeException
└── Law4DevsException base — any SDK error
├── NotFoundError 404 — resource not found
├── ValidationError 400 — invalid parameters
├── RateLimitError 429 — rate limit exceeded
└── ServerError 5xx — server-side error
```
Every exception exposes a `statusCode` property:
```php theme={null}
try {
$article = $client->articles->get('gdpr', 9999);
} catch (\Law4Devs\Exceptions\NotFoundError $e) {
echo 'Not found: ' . $e->getMessage() . "\n"; // message from API
echo 'Status: ' . $e->statusCode . "\n"; // 404
}
```
***
## Catching Specific Errors
```php theme={null}
use Law4Devs\Exceptions\Law4DevsException;
use Law4Devs\Exceptions\NotFoundError;
use Law4Devs\Exceptions\RateLimitError;
use Law4Devs\Exceptions\ValidationError;
try {
$page = $client->articles->list('invalid-framework');
} catch (NotFoundError $e) {
// Framework not found
echo 'Framework not found: ' . $e->getMessage() . "\n";
} catch (ValidationError $e) {
// Bad request parameters
echo 'Invalid request: ' . $e->getMessage() . "\n";
} catch (RateLimitError $e) {
// Retry after a delay (SDK already retried 3 times)
echo 'Rate limit hit: ' . $e->getMessage() . "\n";
} catch (Law4DevsException $e) {
// All other SDK errors (server errors, network failures)
echo 'SDK error (' . $e->statusCode . '): ' . $e->getMessage() . "\n";
}
```
***
## Automatic Retry
The SDK automatically retries on `429 Too Many Requests` and `5xx Server Error` responses using exponential backoff:
| Attempt | Wait |
| --------- | --------- |
| 1st retry | 1 second |
| 2nd retry | 2 seconds |
| 3rd retry | 4 seconds |
After `maxRetries` (default `3`) attempts, the exception is re-thrown.
To disable retries, set `maxRetries: 0`:
```php theme={null}
$client = new \Law4Devs\Client(
apiKey: getenv('LAW4DEVS_API_KEY'),
maxRetries: 0,
);
```
***
## Network Errors
Connection failures (timeout, DNS, SSL) throw `Law4DevsException` with `statusCode` of `null`:
```php theme={null}
use Law4Devs\Exceptions\Law4DevsException;
try {
$page = $client->frameworks->list();
} catch (Law4DevsException $e) {
if ($e->statusCode === null) {
echo 'Network error: ' . $e->getMessage() . "\n";
}
}
```
***
## Best Practices
* Always catch `Law4DevsException` as a fallback to handle unexpected errors
* Check `$e->statusCode` to distinguish network errors (`null`) from API errors
* Wrap `iter()` loops in a try/catch — exceptions can occur mid-iteration if a page request fails
* For batch processing, implement a retry strategy at the application level if you need finer control than the built-in retry
# Frameworks
Source: https://docs.law4devs.eu/sdks/php/frameworks
List and retrieve EU regulatory frameworks with the Law4Devs PHP SDK.
## Overview
Frameworks represent EU regulations such as GDPR, CRA, NIS2, and the AI Act. Use `$client->frameworks` to list and fetch them.
***
## Methods
### `list()`
Returns a page of all available frameworks.
```php theme={null}
$page = $client->frameworks->list();
printf("Found %d frameworks\n", $page->meta->total);
foreach ($page->data as $fw) {
printf(" %-12s %s\n", $fw->slug, $fw->name);
}
```
**Returns** `Page`
***
### `get()`
Fetch a single framework by slug, including detailed counts.
```php theme={null}
$cra = $client->frameworks->get('cra');
echo $cra->name . "\n"; // Cyber Resilience Act
echo $cra->celexNumber . "\n"; // 32024R2847
echo $cra->articleCount . " articles\n";
```
**Parameters**
| Parameter | Type | Description |
| --------- | -------- | ----------------------------- |
| `$slug` | `string` | Framework slug, e.g. `'gdpr'` |
**Returns** `Framework`
***
### `iter()`
Iterate over all frameworks across all pages.
```php theme={null}
foreach ($client->frameworks->iter() as $fw) {
echo $fw->slug . ': ' . $fw->name . "\n";
}
```
**Returns** `\Generator`
***
## Models
### `FrameworkSummary`
Returned by `list()` and `iter()`.
| Field | Type | Description |
| -------------- | -------------- | ---------------------------------------- |
| `id` | `int` | Internal numeric ID |
| `slug` | `string` | URL-safe identifier, e.g. `'cra'` |
| `name` | `string` | Full framework name |
| `shortName` | `string` | Short name, e.g. `'CRA'` |
| `celexNumber` | `string` | EUR-Lex CELEX identifier |
| `description` | `string\|null` | Brief description |
| `isActive` | `bool` | Whether data is actively maintained |
| `status` | `string` | `'active'`, `'draft'`, or `'superseded'` |
| `articleCount` | `int` | Number of synced articles |
| `recitalCount` | `int` | Number of synced recitals |
| `createdAt` | `string` | ISO 8601 creation timestamp |
### `Framework`
Returned by `get()`. Extends `FrameworkSummary` with:
| Field | Type | Description |
| ------------------ | ------------- | --------------------------------- |
| `eurLexUrl` | `string` | Link to the official EUR-Lex page |
| `requirementCount` | `int` | Number of extracted requirements |
| `annexCount` | `int` | Number of annexes |
| `tagCount` | `int` | Number of associated tags |
| `coverage` | `array\|null` | Coverage statistics |
***
## Examples
### List all active frameworks
```php theme={null}
foreach ($client->frameworks->iter() as $fw) {
if ($fw->isActive) {
printf("%-12s %s (%d articles)\n", $fw->slug, $fw->name, $fw->articleCount);
}
}
```
### Find frameworks with full article coverage
```php theme={null}
$page = $client->frameworks->list();
foreach ($page->data as $fw) {
if ($fw->expectedArticles > 0 && $fw->articleCount >= $fw->expectedArticles) {
echo $fw->name . " — fully synced\n";
}
}
```
# Installation
Source: https://docs.law4devs.eu/sdks/php/installation
Install the Law4Devs PHP SDK and make your first API call.
## Requirements
* PHP 8.1 or later
* `ext-curl` and `ext-json` extensions (enabled by default in most PHP installations)
* No runtime dependencies — zero third-party packages required
## Install
```bash theme={null}
composer require law4devs/law4devs
```
To pin to a specific version:
```bash theme={null}
composer require law4devs/law4devs:^0.1
```
## First Call
```php theme={null}
frameworks->list();
echo $page->data[0]->name; // e.g. "Cyber Resilience Act"
```
## Configuration
```php theme={null}
use Law4Devs\Client;
$client = new Client(
apiKey: $_ENV['LAW4DEVS_API_KEY'],
baseUrl: 'https://api.law4devs.eu/v1', // default
timeout: 30, // seconds, default 30
maxRetries: 3, // retries on 429/5xx, default 3
);
```
| Option | Type | Default | Description |
| ------------ | -------- | -------------- | ---------------------------- |
| `apiKey` | `string` | `''` | **Required.** Your API key |
| `baseUrl` | `string` | Production URL | Override for dev environment |
| `timeout` | `int` | `30` | Request timeout in seconds |
| `maxRetries` | `int` | `3` | Retries on 429/5xx |
## Environment Variable
```bash theme={null}
export LAW4DEVS_API_KEY="your-key"
```
```php theme={null}
use Law4Devs\Client;
$client = new Client(apiKey: getenv('LAW4DEVS_API_KEY'));
```
Never hardcode your API key in source code or commit it to version control.
## Verify the Install
```bash theme={null}
LAW4DEVS_API_KEY=your-key php -r "
require 'vendor/autoload.php';
\$c = new \Law4Devs\Client(apiKey: getenv('LAW4DEVS_API_KEY'));
echo \$c->frameworks->list()->meta->total . ' frameworks\n';
"
```
# Pagination
Source: https://docs.law4devs.eu/sdks/php/pagination
How pagination works in the Law4Devs PHP SDK.
## Overview
All `list()` methods return a `Page` object. Every page includes the data array, metadata about the total result set, and links to the next/previous page.
***
## The `Page` Object
```php theme={null}
$page = $client->articles->list('gdpr');
// Data
$articles = $page->data; // ArticleSummary[]
$count = $page->count(); // items on this page
// Metadata
$total = $page->meta->total; // total results across all pages
$current = $page->meta->page; // current page number
$perPage = $page->meta->perPage; // items per page
$pages = $page->meta->pages; // total number of pages
// Links
$nextUrl = $page->links->next; // URL of next page (null if last)
$prevUrl = $page->links->prev; // URL of previous page (null if first)
```
***
## Manual Pagination
Control pagination yourself with `$page` and `$perPage`:
```php theme={null}
$pageNum = 1;
do {
$page = $client->articles->list('gdpr', page: $pageNum, perPage: 50);
foreach ($page->data as $article) {
echo $article->title . "\n";
}
$pageNum++;
} while ($page->links->next !== null);
```
***
## Auto-Pagination with `iter()`
Every resource exposes an `iter()` method that automatically fetches all pages and yields individual items:
```php theme={null}
// Yields ArticleSummary one by one — no manual paging needed
foreach ($client->articles->iter('gdpr') as $article) {
echo $article->articleNumber . ' ' . $article->title . "\n";
}
```
Internally, `iter()` calls `list()` repeatedly, following `links->next` until there are no more pages.
Use `iter()` whenever you need all items. It handles pagination transparently and uses PHP generators, so it doesn't load the full result set into memory at once.
***
## Collecting into an Array
If you need a full array, use `iterator_to_array()`:
```php theme={null}
$articles = iterator_to_array($client->articles->iter('cra'));
echo count($articles) . " total articles\n";
```
***
## Pagination Parameters
| Parameter | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------ |
| `$page` | `int\|null` | `1` | Page number (1-indexed) |
| `$perPage` | `int\|null` | `20` | Items per page (max 100) |
# Quick Start
Source: https://docs.law4devs.eu/sdks/php/quickstart
Make your first Law4Devs API call with PHP in under 60 seconds.
## Install
```bash theme={null}
composer require law4devs/law4devs
```
## Basic Usage
```php theme={null}
frameworks->list();
foreach ($page->data as $fw) {
echo $fw->slug . ' — ' . $fw->name . "\n";
}
// List first 5 CRA articles
$articles = $client->articles->list('cra', perPage: 5);
foreach ($articles->data as $article) {
echo 'Art. ' . $article->articleNumber . ': ' . $article->title . "\n";
}
// Auto-paginate all GDPR articles
foreach ($client->articles->iter('gdpr') as $article) {
echo $article->articleNumber . ' ' . $article->title . "\n";
}
// Search across all frameworks
$results = $client->search->query('data breach notification');
echo 'Found ' . $results->meta->total . " results\n";
```
## Next Steps
# Recitals
Source: https://docs.law4devs.eu/sdks/php/recitals
Fetch and iterate framework recitals with the Law4Devs PHP SDK.
## Overview
Recitals are the numbered preamble paragraphs of EU regulations that provide context and intent for the operative articles. Use `$client->recitals` to access them.
***
## Methods
### `list()`
Returns a page of recitals for the given framework.
```php theme={null}
$page = $client->recitals->list('gdpr', perPage: 10);
printf("GDPR has %d recitals\n", $page->meta->total);
foreach ($page->data as $recital) {
printf(" Recital %s: %.80s...\n", $recital->recitalNumber, $recital->content);
}
```
**Returns** `Page`
***
### `get()`
Fetch a single recital by number.
```php theme={null}
$recital = $client->recitals->get('gdpr', 1);
echo $recital->content . "\n";
```
**Returns** `Recital`
***
### `iter()`
Iterate over all recitals in a framework.
```php theme={null}
foreach ($client->recitals->iter('nis2') as $recital) {
echo $recital->recitalNumber . ' — ' . substr($recital->content, 0, 80) . "\n";
}
```
**Returns** `\Generator`
***
## Model
### `Recital`
| Field | Type | Description |
| --------------- | -------- | ----------------------------------- |
| `id` | `int` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `recitalNumber` | `string` | Recital number within the framework |
| `content` | `string` | Full recital text |
| `position` | `int` | Ordering position in the framework |
# Requirements
Source: https://docs.law4devs.eu/sdks/php/requirements
Fetch extracted compliance requirements with the Law4Devs PHP SDK.
## Overview
Requirements are structured compliance obligations extracted from regulatory articles. Each requirement has a type, optional deadline, linked articles, and stakeholder roles. Use `$client->requirements` to access them.
***
## Methods
### `list()`
Returns a page of requirements for the given framework.
```php theme={null}
$page = $client->requirements->list('cra', perPage: 20);
printf("CRA has %d requirements\n", $page->meta->total);
foreach ($page->data as $req) {
printf(" [%s] %s\n", $req->requirementType, substr($req->requirementText, 0, 80));
}
```
**Returns** `Page`
***
### `get()`
Fetch a single requirement by ID.
```php theme={null}
$req = $client->requirements->get('cra', 42);
echo $req->requirementText . "\n";
```
**Returns** `Requirement`
***
### `iter()`
Iterate over all requirements in a framework.
```php theme={null}
foreach ($client->requirements->iter('gdpr') as $req) {
echo $req->requirementType . ': ' . substr($req->requirementText, 0, 60) . "\n";
}
```
**Returns** `\Generator`
***
## Model
### `Requirement`
| Field | Type | Description |
| ---------------------- | -------------- | ---------------------------------------------- |
| `id` | `int` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `articleNumber` | `int\|null` | Source article number |
| `paragraphRef` | `string\|null` | Paragraph reference within the article |
| `paragraphContent` | `string\|null` | Text of the source paragraph |
| `requirementText` | `string` | The extracted requirement |
| `requirementType` | `string` | Category, e.g. `'obligation'`, `'prohibition'` |
| `complianceDeadline` | `string\|null` | Deadline date (ISO 8601) if specified |
| `linkedArticleNumbers` | `int[]` | Related article numbers |
| `stakeholderRoles` | `string[]` | Affected parties, e.g. `'manufacturer'` |
| `tags` | `Tag[]` | Tags applied to this requirement |
| `createdAt` | `string` | ISO 8601 creation timestamp |
***
## Examples
### Filter by requirement type
```php theme={null}
foreach ($client->requirements->iter('cra') as $req) {
if ($req->requirementType === 'obligation') {
echo '- ' . $req->requirementText . "\n";
}
}
```
### Find requirements with deadlines
```php theme={null}
foreach ($client->requirements->iter('nis2') as $req) {
if ($req->complianceDeadline !== null) {
printf("[%s] %s\n", $req->complianceDeadline, substr($req->requirementText, 0, 80));
}
}
```
# Search
Source: https://docs.law4devs.eu/sdks/php/search
Full-text search across all EU frameworks with the Law4Devs PHP SDK.
## Overview
The search endpoint lets you query across all frameworks simultaneously. Results include articles, recitals, and requirements that match your query. Use `$client->search->query()` to search.
***
## Methods
### `query()`
Search across all frameworks (or a specific one).
```php theme={null}
// Search everything
$results = $client->search->query('incident notification 72 hours');
printf("Found %d results\n", $results->meta->total);
foreach ($results->data as $result) {
printf(" [%s] %s — %s\n",
strtoupper($result->frameworkSlug),
$result->type,
$result->title ?? $result->matchContext,
);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| ------------- | -------------- | -------- | --------------------------------------------------------- |
| `$q` | `string` | required | Search query |
| `$framework` | `string\|null` | `null` | Limit to one framework slug |
| `$resultType` | `string\|null` | `null` | Filter by type: `'article'`, `'recital'`, `'requirement'` |
| `$page` | `int\|null` | `null` | Page number |
| `$perPage` | `int\|null` | `null` | Items per page |
**Returns** `Page`
***
## Model
### `SearchResult`
| Field | Type | Description |
| ----------------- | -------------- | -------------------------------------------- |
| `type` | `string` | `'article'`, `'recital'`, or `'requirement'` |
| `frameworkSlug` | `string` | Framework the result belongs to |
| `frameworkName` | `string` | Human-readable framework name |
| `articleNumber` | `int\|null` | For article results |
| `recitalNumber` | `int\|null` | For recital results |
| `paragraphRef` | `string\|null` | Specific paragraph reference |
| `title` | `string\|null` | Result heading, if any |
| `requirementType` | `string\|null` | For requirement results |
| `matchContext` | `string` | Snippet showing the match in context |
| `url` | `string` | Direct API URL to the full resource |
***
## Examples
### Search across specific frameworks
```php theme={null}
$results = $client->search->query(
q: 'breach notification',
framework: 'gdpr',
);
foreach ($results->data as $r) {
echo $r->frameworkSlug . ' — ' . ($r->title ?? $r->matchContext) . "\n";
}
```
### Filter by result type
```php theme={null}
$results = $client->search->query(
q: 'encryption at rest',
resultType: 'requirement',
);
foreach ($results->data as $r) {
echo '- ' . $r->matchContext . "\n";
}
```
### Cross-framework search
```php theme={null}
$frameworks = ['nis2', 'dora', 'cra'];
foreach ($frameworks as $fw) {
$results = $client->search->query('incident notification', framework: $fw);
printf("%-6s %d results\n", $fw, $results->meta->total);
}
```
Use the `$framework` parameter to narrow results when you know which regulation you're working with. Cross-framework search (without a filter) is great for discovering related obligations across different laws.
# Tags
Source: https://docs.law4devs.eu/sdks/php/tags
List and retrieve taxonomy tags with the Law4Devs PHP SDK.
## Overview
Tags are a structured taxonomy used to categorize articles, recitals, and requirements. Use `$client->tags` to browse all available tags.
***
## Methods
### `list()`
Returns a page of tags, optionally filtered by framework.
```php theme={null}
// All tags
$page = $client->tags->list();
// Tags relevant to CRA
$page = $client->tags->list(frameworkSlug: 'cra');
foreach ($page->data as $tag) {
printf(" %-20s %s\n", $tag->slug, $tag->name);
}
```
**Returns** `Page`
***
### `get()`
Fetch a single tag by slug.
```php theme={null}
$tag = $client->tags->get('data-breach');
echo $tag->name . ': ' . $tag->description . "\n";
```
**Returns** `Tag`
***
### `iter()`
Iterate over all tags.
```php theme={null}
foreach ($client->tags->iter() as $tag) {
echo $tag->slug . "\n";
}
```
**Returns** `\Generator`
***
## Model
### `Tag`
| Field | Type | Description |
| ------------- | -------------- | --------------------------- |
| `id` | `int` | Internal numeric ID |
| `slug` | `string` | URL-safe identifier |
| `name` | `string` | Display name |
| `description` | `string\|null` | What this tag means |
| `keywords` | `string[]` | Related search keywords |
| `color` | `string` | Hex color for display |
| `createdAt` | `string` | ISO 8601 creation timestamp |
# Annexes
Source: https://docs.law4devs.eu/sdks/python/annexes
Fetch and iterate framework annexes with the Law4Devs Python SDK.
## Overview
Annexes are supplementary parts of EU legislation, often containing technical specifications, lists, or tables that are referenced within the articles. Each annex belongs to one framework and is identified by its annex number (or letter, e.g. `"I"`, `"II"`, `"A"`).
***
## Methods
### `list()`
Returns a page of annex summaries for a framework.
```python theme={null}
page = client.annexes.list("cra", page=1, per_page=20)
print(f"CRA has {page.meta.total} annexes")
for annex in page.data:
print(f" Annex {annex.annex_number}: {annex.title}")
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------------- | ----- | -------- | ---------------------------- |
| `framework_slug` | `str` | required | Framework slug, e.g. `"cra"` |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[AnnexSummary]`
***
### `get()`
Fetch the full content of a single annex.
```python theme={null}
annex = client.annexes.get("cra", "I")
print(annex.title)
print(annex.content)
```
**Parameters**
| Parameter | Type | Description |
| ---------------- | ----- | ------------------------------------------- |
| `framework_slug` | `str` | Framework slug |
| `annex_number` | `str` | Annex identifier, e.g. `"I"`, `"II"`, `"A"` |
**Returns** `Annex`
***
### `iter()`
Iterate over all annexes in a framework.
```python theme={null}
for annex in client.annexes.iter("cra"):
print(f"Annex {annex.annex_number}: {annex.title}")
```
**Parameters** — same kwargs as `list()` (except `page`).
**Returns** `Iterator[AnnexSummary]`
***
## Models
### `AnnexSummary`
Returned by `list()` and `iter()`.
| Field | Type | Description |
| ---------------- | ------------- | -------------------------------------- |
| `id` | `int` | Internal numeric ID |
| `framework_slug` | `str` | Parent framework slug |
| `annex_number` | `str` | Annex identifier, e.g. `"I"` |
| `title` | `str \| None` | Annex heading |
| `position` | `int` | Ordering position within the framework |
| `section_count` | `int` | Number of sections within this annex |
### `Annex`
Returned by `get()`. Extends `AnnexSummary` with:
| Field | Type | Description |
| ---------- | -------------------- | ------------------------------------ |
| `content` | `str \| None` | Full plain-text content of the annex |
| `sections` | `list[AnnexSection]` | Structured sections, if parsed |
### `AnnexSection`
| Field | Type | Description |
| ---------------- | ----- | ---------------------------------- |
| `id` | `int` | Internal numeric ID |
| `section_number` | `str` | Section label, e.g. `"1"`, `"1.1"` |
| `content` | `str` | Section text |
| `position` | `int` | Ordering position within the annex |
***
## Examples
### List all CRA annexes
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
for annex in client.annexes.iter("cra"):
print(f"Annex {annex.annex_number}: {annex.title or '(untitled)'} ({annex.section_count} sections)")
```
### Read the full content of an annex
```python theme={null}
annex = client.annexes.get("cra", "I")
print(f"=== {annex.title} ===\n")
if annex.sections:
for section in annex.sections:
print(f"\n{section.section_number}. {section.content}")
else:
print(annex.content)
```
### Check which frameworks have annexes
```python theme={null}
for fw in client.frameworks.iter():
detail = client.frameworks.get(fw.slug)
if detail.annex_count > 0:
print(f"{fw.short_name}: {detail.annex_count} annexes")
```
### Collect all annex content for a framework
```python theme={null}
all_content = []
for annex in client.annexes.iter("gdpr"):
full = client.annexes.get("gdpr", annex.annex_number)
all_content.append({
"annex": annex.annex_number,
"title": annex.title,
"content": full.content,
})
print(f"Fetched {len(all_content)} GDPR annexes")
```
Annex numbers follow the official EU publication format. Roman numerals (`"I"`, `"II"`) are most common, but some frameworks use letters or Arabic numerals.
# Articles
Source: https://docs.law4devs.eu/sdks/python/articles
Fetch and iterate framework articles with the Law4Devs Python SDK.
## Overview
Articles are the primary legal content of each framework. Every article belongs to exactly one framework, identified by its `framework_slug`. Use `client.articles` to list, fetch, and navigate them.
***
## Methods
### `list()`
Returns a page of article summaries for the given framework.
```python theme={null}
page = client.articles.list("gdpr", page=1, per_page=20)
print(f"GDPR has {page.meta.total} articles")
for article in page.data:
print(f" Art. {article.article_number}: {article.title}")
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------------- | ----- | -------- | ----------------------------- |
| `framework_slug` | `str` | required | Framework slug, e.g. `"gdpr"` |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[ArticleSummary]`
***
### `get()`
Fetch the full content of a single article, including all paragraphs.
```python theme={null}
article = client.articles.get("gdpr", 17)
print(article.title)
print(article.content)
for para in article.paragraphs:
print(f" §{para.paragraph_number}: {para.content}")
```
**Parameters**
| Parameter | Type | Description |
| ---------------- | ----- | ----------------------------------- |
| `framework_slug` | `str` | Framework slug |
| `article_number` | `int` | Article number within the framework |
**Returns** `Article`
***
### `related()`
Fetch articles related to a given article (linked via shared requirements or tags).
```python theme={null}
page = client.articles.related("gdpr", 17)
for article in page.data:
print(f" Related: Art. {article.article_number} — {article.title}")
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------------- | ----- | -------- | ---------------------- |
| `framework_slug` | `str` | required | Framework slug |
| `article_number` | `int` | required | Article number |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[ArticleSummary]`
***
### `iter()`
Iterate over all articles in a framework across all pages.
```python theme={null}
for article in client.articles.iter("gdpr"):
print(f"Art. {article.article_number}: {article.title}")
```
**Parameters** — same kwargs as `list()` (except `page`).
**Returns** `Iterator[ArticleSummary]`
***
## Models
### `ArticleSummary`
Returned by `list()`, `related()`, and `iter()`.
| Field | Type | Description |
| ----------------- | ------------- | ----------------------------------- |
| `id` | `int` | Internal numeric ID |
| `framework_slug` | `str` | Parent framework slug |
| `article_number` | `int` | Article number within the framework |
| `title` | `str \| None` | Article heading |
| `position` | `int` | Ordering position in the framework |
| `paragraph_count` | `int` | Number of paragraphs |
| `tags` | `list[str]` | Tag slugs applied to this article |
### `Article`
Returned by `get()`. Extends `ArticleSummary` with:
| Field | Type | Description |
| ------------ | ----------------- | -------------------------------------- |
| `content` | `str \| None` | Full plain-text content of the article |
| `paragraphs` | `list[Paragraph]` | Ordered list of paragraph objects |
### `Paragraph`
| Field | Type | Description |
| ------------------ | ----- | ------------------------------------ |
| `id` | `int` | Internal numeric ID |
| `paragraph_number` | `str` | Label, e.g. `"1"`, `"2a"`, `"intro"` |
| `content` | `str` | Paragraph text |
| `position` | `int` | Ordering position within the article |
***
## Examples
### Print every GDPR chapter with article titles
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
for article in client.articles.iter("gdpr"):
print(f"Art. {article.article_number:>3} {article.title or '(untitled)'}")
```
### Fetch a specific article and display it
```python theme={null}
article = client.articles.get("gdpr", 17)
print(f"Article {article.article_number}: {article.title}")
print("=" * 60)
for para in article.paragraphs:
print(f"\n({para.paragraph_number}) {para.content}")
```
### Find all articles tagged with a specific tag
```python theme={null}
tagged = [
a for a in client.articles.iter("gdpr")
if "data-breach" in a.tags
]
print(f"{len(tagged)} articles tagged 'data-breach'")
```
### Cross-reference related articles
```python theme={null}
article = client.articles.get("gdpr", 33) # Personal data breach notification
related = client.articles.related("gdpr", 33)
print(f"Art. 33 is related to {related.meta.total} other articles:")
for r in related.data:
print(f" Art. {r.article_number}: {r.title}")
```
### Collect all articles into a dict for fast lookup
```python theme={null}
articles_by_number = {
a.article_number: a
for a in client.articles.iter("cra")
}
print(articles_by_number[10].title)
```
Use `iter()` when you need every article. Use `list()` when you only need one page or want to control pagination yourself.
# Compliance Deadlines
Source: https://docs.law4devs.eu/sdks/python/compliance
Query compliance deadlines across EU frameworks with the Law4Devs Python SDK.
## Overview
Compliance deadlines are specific dates by which organizations must satisfy a legal obligation. Each deadline is extracted from a framework article and classified by type (e.g. `"transposition"`, `"application"`, `"notification"`).
***
## Methods
### `deadlines()`
Returns a page of compliance deadlines. Without filters, returns deadlines across all frameworks.
```python theme={null}
# All deadlines
page = client.compliance.deadlines()
print(f"Total deadlines: {page.meta.total}")
# Filter by framework
page = client.compliance.deadlines(framework_slug="nis2")
for dl in page.data:
print(f"{dl.deadline_date} [{dl.deadline_type}] {dl.description}")
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------------- | ----- | ------- | ------------------------------ |
| `framework_slug` | `str` | `None` | Filter to a specific framework |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[ComplianceDeadline]`
***
### `iter_deadlines()`
Iterate over all deadlines, optionally filtered by framework.
```python theme={null}
for deadline in client.compliance.iter_deadlines(framework_slug="nis2"):
print(f"{deadline.deadline_date}: {deadline.description}")
```
**Parameters** — same kwargs as `deadlines()` (except `page`).
**Returns** `Iterator[ComplianceDeadline]`
***
## Model
### `ComplianceDeadline`
| Field | Type | Description |
| ---------------- | ------------- | ------------------------------------------------------------------------------ |
| `id` | `int` | Internal numeric ID |
| `framework_slug` | `str` | Parent framework slug |
| `article_number` | `int \| None` | Source article number |
| `paragraph_ref` | `str \| None` | Source paragraph label |
| `deadline_date` | `str` | ISO 8601 date, e.g. `"2024-10-17"` |
| `deadline_type` | `str` | `"transposition"`, `"application"`, `"notification"`, `"review"`, or `"other"` |
| `description` | `str` | Plain-text description of the obligation |
***
## Examples
### Print upcoming deadlines sorted by date
```python theme={null}
from law4devs import Law4DevsClient
from datetime import date
client = Law4DevsClient()
today = date.today().isoformat()
upcoming = sorted(
[dl for dl in client.compliance.iter_deadlines() if dl.deadline_date >= today],
key=lambda d: d.deadline_date,
)
print("Upcoming compliance deadlines:")
for dl in upcoming:
print(f" {dl.deadline_date} [{dl.framework_slug}] {dl.description[:60]}")
```
### Deadlines for a specific framework
```python theme={null}
print("NIS2 compliance deadlines:")
for dl in client.compliance.iter_deadlines(framework_slug="nis2"):
source = f"Art. {dl.article_number}" if dl.article_number else "general"
print(f" {dl.deadline_date} ({source}) {dl.description}")
```
### Group deadlines by type
```python theme={null}
from collections import defaultdict
by_type = defaultdict(list)
for dl in client.compliance.iter_deadlines():
by_type[dl.deadline_type].append(dl)
for dtype, deadlines in sorted(by_type.items()):
print(f"{dtype}: {len(deadlines)} deadlines")
```
### Export a deadline calendar to JSON
```python theme={null}
import json
calendar = []
for dl in client.compliance.iter_deadlines():
calendar.append({
"date": dl.deadline_date,
"framework": dl.framework_slug,
"type": dl.deadline_type,
"description": dl.description,
"source_article": dl.article_number,
})
calendar.sort(key=lambda x: x["date"])
with open("compliance_calendar.json", "w") as f:
json.dump(calendar, f, indent=2)
print(f"Exported {len(calendar)} deadlines")
```
### Check if a specific deadline has passed
```python theme={null}
from datetime import date
dl = client.compliance.deadlines(framework_slug="nis2").data[0]
deadline_date = date.fromisoformat(dl.deadline_date)
if deadline_date < date.today():
print(f"OVERDUE: {dl.description}")
else:
days_left = (deadline_date - date.today()).days
print(f"{days_left} days until: {dl.description}")
```
Deadline dates are extracted from the official legislative text and may not reflect national transposition extensions. Always verify against the official EUR-Lex publication for legal certainty.
# Error Handling
Source: https://docs.law4devs.eu/sdks/python/error-handling
Handle API errors gracefully with the Law4Devs Python SDK exception hierarchy.
## Overview
All SDK errors inherit from `Law4DevsError`. Each exception class maps to a specific HTTP status code or error condition. The SDK automatically retries transient errors (429 and 5xx) with exponential backoff before raising.
***
## Exception Hierarchy
```
Law4DevsError
├── NotFoundError (HTTP 404)
├── ValidationError (HTTP 400 / 422)
├── RateLimitError (HTTP 429)
└── ServerError (HTTP 500 / 502 / 503 / 504)
```
All exceptions expose a `status_code` attribute containing the underlying HTTP status code.
```python theme={null}
from law4devs.exceptions import (
Law4DevsError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
)
```
***
## Exception Reference
### `Law4DevsError`
Base class for all SDK exceptions. Catch this to handle any API error.
| Attribute | Type | Description |
| ------------- | ----- | ---------------------------- |
| `status_code` | `int` | HTTP status code |
| `message` | `str` | Human-readable error message |
### `NotFoundError`
Raised when the requested resource does not exist (HTTP 404).
```python theme={null}
try:
article = client.articles.get("gdpr", 9999)
except NotFoundError as e:
print(f"Article not found: {e.message}")
```
### `ValidationError`
Raised when the request parameters are invalid (HTTP 400 or 422).
```python theme={null}
try:
page = client.articles.list("gdpr", per_page=999) # exceeds max
except ValidationError as e:
print(f"Bad request: {e.message}")
```
### `RateLimitError`
Raised when the rate limit is exceeded and all retries are exhausted (HTTP 429).
```python theme={null}
try:
results = client.search.query("encryption")
except RateLimitError as e:
print(f"Rate limited. Retry after: {e.retry_after} seconds")
```
### `ServerError`
Raised when the server returns a 5xx response and all retries are exhausted.
```python theme={null}
try:
page = client.frameworks.list()
except ServerError as e:
print(f"Server error {e.status_code}: {e.message}")
```
***
## Auto-Retry Behaviour
The SDK automatically retries failed requests on HTTP 429 and 5xx responses. Retries use exponential backoff with jitter.
* **Default retries**: 3
* **Backoff**: 1s, 2s, 4s (with jitter)
* **Retried status codes**: 429, 500, 502, 503, 504
If all retries are exhausted, the appropriate exception (`RateLimitError` or `ServerError`) is raised.
Configure the retry count at construction time:
```python theme={null}
from law4devs import Law4DevsClient
# Disable retries
client = Law4DevsClient(max_retries=0)
# More retries for a long-running batch job
client = Law4DevsClient(max_retries=5)
```
***
## Practical Patterns
### Handle a missing resource
```python theme={null}
from law4devs import Law4DevsClient
from law4devs.exceptions import NotFoundError
client = Law4DevsClient()
try:
article = client.articles.get("gdpr", 999)
except NotFoundError:
print("That article doesn't exist")
```
### `safe_get` helper
A utility that returns `None` instead of raising on 404:
```python theme={null}
from law4devs.exceptions import NotFoundError
def safe_get_article(client, framework_slug, article_number):
try:
return client.articles.get(framework_slug, article_number)
except NotFoundError:
return None
article = safe_get_article(client, "gdpr", 999)
if article is None:
print("Not found")
else:
print(article.title)
```
### Catch all API errors
```python theme={null}
from law4devs.exceptions import Law4DevsError
try:
page = client.frameworks.list()
except Law4DevsError as e:
print(f"API error {e.status_code}: {e.message}")
```
### Handle rate limits explicitly
```python theme={null}
import time
from law4devs.exceptions import RateLimitError
def fetch_with_backoff(client, framework_slug):
for attempt in range(5):
try:
return list(client.articles.iter(framework_slug))
except RateLimitError as e:
wait = getattr(e, "retry_after", 2 ** attempt)
print(f"Rate limited. Waiting {wait}s (attempt {attempt + 1}/5)")
time.sleep(wait)
raise RuntimeError("Exhausted retries")
```
### Distinguish error types
```python theme={null}
from law4devs.exceptions import (
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
Law4DevsError,
)
try:
result = client.articles.get("gdpr", article_number)
except NotFoundError:
print("Article not found — check the article number")
except ValidationError as e:
print(f"Invalid parameters: {e.message}")
except RateLimitError:
print("Too many requests — slow down or add an API key")
except ServerError as e:
print(f"Server problem ({e.status_code}) — try again later")
except Law4DevsError as e:
print(f"Unexpected error {e.status_code}: {e.message}")
```
### Validate a framework slug before use
```python theme={null}
from law4devs.exceptions import NotFoundError
def framework_exists(client, slug):
try:
client.frameworks.get(slug)
return True
except NotFoundError:
return False
if not framework_exists(client, "unknown-framework"):
print("Framework not found")
```
***
## Timeout Errors
Network timeouts raise Python's built-in `TimeoutError` (not a `Law4DevsError`). Configure the timeout per client:
```python theme={null}
# Allow up to 60 seconds per request
client = Law4DevsClient(timeout=60)
```
```python theme={null}
try:
page = client.frameworks.list()
except TimeoutError:
print("Request timed out — the server may be slow")
```
For batch jobs that process thousands of items, set `max_retries=5` and a generous `timeout=60`. The SDK will handle intermittent failures automatically.
404 responses are **not** retried automatically — they indicate a resource genuinely does not exist, not a transient failure.
# Frameworks
Source: https://docs.law4devs.eu/sdks/python/frameworks
List, inspect, and iterate EU regulatory frameworks with the Law4Devs Python SDK.
## Overview
Frameworks are the top-level resource — every article, recital, requirement, and annex belongs to one. Use the `client.frameworks` resource to query them.
***
## Methods
### `list()`
Returns the first page of frameworks.
```python theme={null}
page = client.frameworks.list(page=1, per_page=20)
print(page.meta.total) # total number of frameworks
for fw in page.data:
print(fw.slug, fw.name)
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------- | ----- | ------- | ---------------------- |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[Framework]`
***
### `get()`
Fetch a single framework by its slug.
```python theme={null}
fw = client.frameworks.get("gdpr")
print(fw.name) # "General Data Protection Regulation"
print(fw.eurlex_url) # "https://eur-lex.europa.eu/..."
print(fw.requirement_count)
print(fw.coverage) # dict with sync coverage info
```
**Parameters**
| Parameter | Type | Description |
| --------- | ----- | ------------------------------------------------ |
| `slug` | `str` | Framework slug, e.g. `"gdpr"`, `"cra"`, `"nis2"` |
**Returns** `FrameworkDetail`
***
### `iter()`
Iterate over every framework across all pages without manual pagination.
```python theme={null}
for fw in client.frameworks.iter():
print(fw.slug, fw.name)
```
**Parameters** — same kwargs as `list()` (except `page`, which is managed internally).
**Returns** `Iterator[Framework]`
***
## Models
### `Framework`
Returned by `list()` and `iter()`.
| Field | Type | Description |
| ------------------- | ------------- | ---------------------------------------- |
| `id` | `int` | Internal numeric ID |
| `slug` | `str` | URL-safe identifier, e.g. `"gdpr"` |
| `name` | `str` | Full official name |
| `short_name` | `str` | Abbreviated name, e.g. `"GDPR"` |
| `celex_number` | `str \| None` | EUR-Lex CELEX identifier |
| `description` | `str \| None` | Plain-text summary |
| `is_active` | `bool` | Whether the framework is in force |
| `status` | `str` | `"active"`, `"draft"`, or `"superseded"` |
| `expected_articles` | `int \| None` | Known article count from official text |
| `expected_recitals` | `int \| None` | Known recital count |
| `last_synced_at` | `str \| None` | ISO 8601 timestamp of last data sync |
| `created_at` | `str` | ISO 8601 timestamp of record creation |
| `article_count` | `int` | Articles currently in the database |
| `recital_count` | `int` | Recitals currently in the database |
### `FrameworkDetail`
Returned by `get()`. Extends `Framework` with:
| Field | Type | Description |
| ------------------- | ------------- | ---------------------------------------- |
| `eurlex_url` | `str \| None` | Canonical EUR-Lex URL |
| `requirement_count` | `int` | Parsed compliance requirements |
| `annex_count` | `int` | Annexes in the database |
| `tag_count` | `int` | Tags applied to this framework's content |
| `coverage` | `dict` | Sync coverage statistics |
***
## Examples
### Print a framework summary table
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
print(f"{'Slug':<12} {'Name':<45} {'Articles':>8} {'Status'}")
print("-" * 75)
for fw in client.frameworks.iter():
print(f"{fw.slug:<12} {fw.name:<45} {fw.article_count:>8} {fw.status}")
```
### Filter active frameworks only
```python theme={null}
active = [fw for fw in client.frameworks.iter() if fw.status == "active"]
print(f"{len(active)} active frameworks")
```
### Inspect a framework's coverage
```python theme={null}
fw = client.frameworks.get("cra")
print(f"CRA: {fw.article_count}/{fw.expected_articles} articles synced")
print(f" {fw.recital_count}/{fw.expected_recitals} recitals synced")
```
### Look up a framework by short name
```python theme={null}
all_frameworks = list(client.frameworks.iter())
match = next((f for f in all_frameworks if f.short_name == "NIS2"), None)
if match:
print(match.slug) # "nis2"
```
Framework slugs are stable identifiers — safe to hardcode in your application. They will not change between API versions.
Some frameworks have `status="superseded"` (e.g. NIS1). The data is still accessible and browsable, but these frameworks are no longer in force.
# Installation
Source: https://docs.law4devs.eu/sdks/python/installation
Install the Law4Devs Python SDK and make your first API call.
## Requirements
* Python 3.9 or later
* No runtime dependencies — the SDK uses only the Python standard library (`urllib`)
## Install
```bash theme={null}
pip install law4devs
```
To pin to a specific version:
```bash theme={null}
pip install law4devs==1.0.0
```
## First Call
```python theme={null}
import os
from law4devs import Law4DevsClient
client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"])
page = client.frameworks.list()
print(page.data[0].name) # e.g. "Cyber Resilience Act"
```
## Configuration
```python theme={null}
import os
from law4devs import Law4DevsClient
client = Law4DevsClient(
api_key=os.environ["LAW4DEVS_API_KEY"],
base_url="https://api.law4devs.eu/v1", # default
timeout=30, # seconds, default 30
max_retries=3, # retries on 429/5xx, default 3
)
```
| Option | Type | Default | Description |
| ------------- | ----- | -------------- | ---------------------------- |
| `api_key` | `str` | — | **Required.** Your API key |
| `base_url` | `str` | Production URL | Override for dev environment |
| `timeout` | `int` | `30` | Request timeout in seconds |
| `max_retries` | `int` | `3` | Retries on 429/5xx |
## Environment Variable
```bash theme={null}
export LAW4DEVS_API_KEY="your-key"
```
```python theme={null}
import os
from law4devs import Law4DevsClient
client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"])
```
Never hardcode your API key in source code or commit it to version control.
## Verify the Install
```bash theme={null}
LAW4DEVS_API_KEY=your-key python -c "
from law4devs import Law4DevsClient
import os
c = Law4DevsClient(api_key=os.environ['LAW4DEVS_API_KEY'])
print(c.frameworks.list().meta.total, 'frameworks')
"
```
# Pagination
Source: https://docs.law4devs.eu/sdks/python/pagination
Manual pagination and auto-pagination with iter() in the Law4Devs Python SDK.
## Overview
Every list endpoint returns a `Page[T]` object. You can either paginate manually using the `.links` and `.meta` fields, or let the SDK handle it automatically with `iter()`.
***
## The `Page[T]` Object
```python theme={null}
page = client.frameworks.list()
# The items on this page
page.data # list[Framework]
# Metadata about the full result set
page.meta.total # int — total items across all pages
page.meta.page # int — current page number (1-indexed)
page.meta.per_page # int — items per page
page.meta.pages # int — total number of pages
# Navigation links (None if not applicable)
page.links.next # str | None — URL of the next page
page.links.prev # str | None — URL of the previous page
```
***
## Manual Pagination
Loop through pages yourself by checking `page.links.next`:
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
page_num = 1
while True:
page = client.frameworks.list(page=page_num, per_page=10)
for fw in page.data:
print(fw.name)
if page.links.next is None:
break
page_num += 1
print(f"Done. Total frameworks: {page.meta.total}")
```
***
## Auto-Pagination with `iter()`
Every resource exposes an `iter()` method that fetches pages on demand and yields items one at a time. This is the recommended approach for processing complete result sets.
### Iterate all frameworks
```python theme={null}
for fw in client.frameworks.iter():
print(fw.slug, fw.name)
```
### Iterate all articles in a framework
```python theme={null}
for article in client.articles.iter("gdpr"):
print(f"Art. {article.article_number}: {article.title}")
```
### Iterate with a custom page size
Use `per_page` to control how many items are fetched per HTTP request. Larger values reduce the number of requests but use more memory per page.
```python theme={null}
# Fetch 100 articles per request instead of the default 20
for article in client.articles.iter("gdpr", per_page=100):
print(article.article_number)
```
***
## Collecting Results into a List
```python theme={null}
# Collect all requirements for a framework into a list
all_requirements = list(client.requirements.iter(framework_slug="gdpr"))
print(f"Loaded {len(all_requirements)} requirements")
```
For large frameworks, collecting everything into a list can use significant memory. Prefer streaming with a `for` loop when you don't need random access.
***
## The `per_page` Parameter
All `list()` and `iter()` methods accept a `per_page` parameter:
* Minimum: `1`
* Maximum: `100`
* Default: `20`
```python theme={null}
# Smallest possible pages (useful for sampling)
page = client.articles.list("gdpr", per_page=1)
print(f"GDPR has {page.meta.total} articles total")
# Largest pages (fewer HTTP requests)
for article in client.articles.iter("cra", per_page=100):
process(article)
```
***
## Checking Total Count Without Fetching All Items
Request a single item to read `meta.total` without loading the full data set:
```python theme={null}
page = client.requirements.list(framework_slug="nis2", per_page=1)
print(f"NIS2 has {page.meta.total} requirements")
```
***
## Detailed Examples
### Process all frameworks with progress reporting
```python theme={null}
frameworks = list(client.frameworks.iter())
total = len(frameworks)
for i, fw in enumerate(frameworks, 1):
print(f"[{i}/{total}] Processing {fw.slug}...")
articles = list(client.articles.iter(fw.slug))
print(f" {len(articles)} articles")
```
### Collect all requirements across all frameworks
```python theme={null}
all_requirements = []
for fw in client.frameworks.iter():
for req in client.requirements.iter(framework_slug=fw.slug):
all_requirements.append(req)
print(f"Total requirements across all frameworks: {len(all_requirements)}")
```
### Paginate with an offset (skip first N items)
```python theme={null}
# Skip the first 40 items (pages 1–2 at per_page=20) and start from page 3
page = client.articles.list("gdpr", page=3, per_page=20)
for article in page.data:
print(article.article_number)
```
### Stop iteration early
Because `iter()` yields lazily, you can stop at any point without fetching the remaining pages:
```python theme={null}
import itertools
# Only process the first 50 articles, regardless of total count
for article in itertools.islice(client.articles.iter("gdpr"), 50):
print(article.title)
```
`iter()` is lazy — it only fetches the next page when the current page's items are exhausted. If you break out of the loop early, no unnecessary HTTP requests are made.
# Quick Start
Source: https://docs.law4devs.eu/sdks/python/quickstart
Build your first Law4Devs integration in Python in under five minutes.
## Prerequisites
Install the SDK first:
```bash theme={null}
pip install law4devs
```
Then create a client:
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
```
***
## List All Frameworks
Fetch the first page of available EU regulatory frameworks:
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
page = client.frameworks.list()
print(f"Total frameworks: {page.meta.total}")
for fw in page.data:
print(f" {fw.slug}: {fw.name} ({fw.article_count} articles)")
```
Example output:
```
Total frameworks: 19
gdpr: General Data Protection Regulation (99 articles)
cra: Cyber Resilience Act (71 articles)
nis2: Network and Information Security Directive 2 (46 articles)
...
```
***
## Iterate All CRA Articles
Use `iter()` to walk every article without managing pagination yourself:
```python theme={null}
for article in client.articles.iter("cra"):
print(f"Art. {article.article_number}: {article.title}")
```
`iter()` automatically fetches subsequent pages until the list is exhausted.
***
## Fetch a Single Article
```python theme={null}
article = client.articles.get("gdpr", 17)
print(article.title)
# "Right to erasure ('right to be forgotten')"
for para in article.paragraphs:
print(f" §{para.paragraph_number}: {para.content[:80]}...")
```
***
## Search Across All Frameworks
```python theme={null}
results = client.search.query("data breach notification", framework="gdpr")
for r in results.data:
print(r.match_context)
```
Narrow by result type:
```python theme={null}
results = client.search.query("incident response", result_type="article")
```
***
## Get Compliance Requirements
Pull every GDPR requirement and print its type:
```python theme={null}
for req in client.requirements.iter(framework_slug="gdpr"):
print(f"[{req.requirement_type}] Art. {req.article_number} — {req.requirement_text[:60]}...")
```
***
## Get Compliance Deadlines
```python theme={null}
for deadline in client.compliance.iter_deadlines(framework_slug="nis2"):
print(f"{deadline.deadline_date}: {deadline.description}")
```
***
## Next Steps
# Recitals
Source: https://docs.law4devs.eu/sdks/python/recitals
Fetch and iterate framework recitals with the Law4Devs Python SDK.
## Overview
Recitals are the preamble paragraphs of EU legislation. They provide interpretive context for the articles that follow and are often cited when explaining the intent behind a legal requirement. Each recital belongs to one framework.
***
## Methods
### `list()`
Returns a page of recitals for a framework.
```python theme={null}
page = client.recitals.list("gdpr", page=1, per_page=20)
print(f"GDPR has {page.meta.total} recitals")
for recital in page.data:
print(f" Recital {recital.recital_number}: {recital.content[:80]}...")
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------------- | ----- | -------- | ----------------------------- |
| `framework_slug` | `str` | required | Framework slug, e.g. `"gdpr"` |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[Recital]`
***
### `get()`
Fetch a single recital by its number.
```python theme={null}
recital = client.recitals.get("gdpr", 26)
print(f"Recital {recital.recital_number}")
print(recital.content)
```
**Parameters**
| Parameter | Type | Description |
| ---------------- | ----- | ----------------------------------- |
| `framework_slug` | `str` | Framework slug |
| `recital_number` | `int` | Recital number within the framework |
**Returns** `Recital`
***
### `iter()`
Iterate over all recitals in a framework across all pages.
```python theme={null}
for recital in client.recitals.iter("gdpr"):
print(f"({recital.recital_number}) {recital.content[:120]}...")
```
**Parameters** — same kwargs as `list()` (except `page`).
**Returns** `Iterator[Recital]`
***
## Model
### `Recital`
| Field | Type | Description |
| ---------------- | ----- | ----------------------------------- |
| `id` | `int` | Internal numeric ID |
| `framework_slug` | `str` | Parent framework slug |
| `recital_number` | `int` | Recital number within the framework |
| `content` | `str` | Full text of the recital |
| `position` | `int` | Ordering position in the framework |
***
## Examples
### Print all GDPR recitals
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
for r in client.recitals.iter("gdpr"):
print(f"({r.recital_number}) {r.content}\n")
```
### Search recital text for a keyword
```python theme={null}
keyword = "consent"
matches = [
r for r in client.recitals.iter("gdpr")
if keyword.lower() in r.content.lower()
]
print(f"{len(matches)} GDPR recitals mention '{keyword}':")
for r in matches:
print(f" Recital {r.recital_number}")
```
### Fetch a specific recital
```python theme={null}
# Recital 26 is the famous "not a natural person" recital
r = client.recitals.get("gdpr", 26)
print(r.content)
```
### Count recitals per framework
```python theme={null}
frameworks = list(client.frameworks.iter())
for fw in frameworks:
page = client.recitals.list(fw.slug, per_page=1)
print(f"{fw.short_name}: {page.meta.total} recitals")
```
Recitals are numbered sequentially within each framework. Recital numbers are stable and match the official EUR-Lex publication.
# Requirements
Source: https://docs.law4devs.eu/sdks/python/requirements
Query parsed compliance requirements from EU frameworks using the Law4Devs Python SDK.
## Overview
Requirements are machine-readable compliance obligations extracted from framework articles. Each requirement is tied to a specific article (and optionally a paragraph), carries a type classification, and lists the stakeholder roles it applies to.
***
## Methods
### `list()`
Returns a page of requirements. Without filters, this returns requirements across all frameworks.
```python theme={null}
# All requirements (all frameworks)
page = client.requirements.list()
print(f"Total requirements: {page.meta.total}")
# Filter to a single framework
page = client.requirements.list(framework_slug="gdpr")
for req in page.data:
print(f"[{req.requirement_type}] Art. {req.article_number}: {req.requirement_text[:60]}...")
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------------- | ----- | ------- | ---------------------- |
| `framework_slug` | `str` | `None` | Filter by framework |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[Requirement]`
***
### `iter()`
Iterate over all requirements, optionally filtered by framework.
```python theme={null}
for req in client.requirements.iter(framework_slug="cra"):
print(req.requirement_text)
```
**Parameters** — same kwargs as `list()` (except `page`).
**Returns** `Iterator[Requirement]`
***
## Model
### `Requirement`
| Field | Type | Description |
| ------------------------ | ------------- | ------------------------------------------------------------------------------------- |
| `id` | `int` | Internal numeric ID |
| `framework_slug` | `str` | Parent framework slug |
| `article_number` | `int` | Source article number |
| `paragraph_ref` | `str \| None` | Paragraph label, e.g. `"1"`, `"2a"` |
| `paragraph_content` | `str \| None` | Full text of the source paragraph |
| `requirement_text` | `str` | Extracted obligation text |
| `requirement_type` | `str` | Classification: `"obligation"`, `"prohibition"`, `"permission"`, `"definition"`, etc. |
| `compliance_deadline` | `str \| None` | ISO 8601 date if a deadline is specified |
| `linked_article_numbers` | `list[int]` | Related articles within the same framework |
| `stakeholder_roles` | `list[str]` | Roles this applies to, e.g. `["controller", "processor"]` |
| `tags` | `list[str]` | Tag slugs |
| `created_at` | `str` | ISO 8601 creation timestamp |
***
## Examples
### Build a compliance checklist
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
print("GDPR Compliance Checklist")
print("=" * 60)
obligations = [
req for req in client.requirements.iter(framework_slug="gdpr")
if req.requirement_type == "obligation"
]
for i, req in enumerate(obligations, 1):
roles = ", ".join(req.stakeholder_roles) or "all parties"
print(f"\n[{i}] Art. {req.article_number} — {roles}")
print(f" {req.requirement_text}")
if req.compliance_deadline:
print(f" Deadline: {req.compliance_deadline}")
```
### Filter requirements for a specific role
```python theme={null}
controller_reqs = [
req for req in client.requirements.iter(framework_slug="gdpr")
if "controller" in req.stakeholder_roles
]
print(f"GDPR has {len(controller_reqs)} requirements for data controllers")
```
### Find requirements with deadlines
```python theme={null}
with_deadlines = [
req for req in client.requirements.iter()
if req.compliance_deadline is not None
]
for req in sorted(with_deadlines, key=lambda r: r.compliance_deadline):
print(f"{req.compliance_deadline} [{req.framework_slug}] Art. {req.article_number}")
```
### Group requirements by type
```python theme={null}
from collections import defaultdict
by_type = defaultdict(list)
for req in client.requirements.iter(framework_slug="nis2"):
by_type[req.requirement_type].append(req)
for req_type, reqs in sorted(by_type.items()):
print(f"{req_type}: {len(reqs)} requirements")
```
### Export requirements to a CSV
```python theme={null}
import csv
with open("gdpr_requirements.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=[
"article_number", "paragraph_ref", "requirement_type",
"stakeholder_roles", "requirement_text",
])
writer.writeheader()
for req in client.requirements.iter(framework_slug="gdpr"):
writer.writerow({
"article_number": req.article_number,
"paragraph_ref": req.paragraph_ref or "",
"requirement_type": req.requirement_type,
"stakeholder_roles": "; ".join(req.stakeholder_roles),
"requirement_text": req.requirement_text,
})
print("Exported to gdpr_requirements.csv")
```
The `requirement_type` field makes it easy to separate hard obligations from definitions and permissions. Filter on `"obligation"` or `"prohibition"` when building audit checklists.
`linked_article_numbers` contains article numbers within the same framework, not cross-framework references.
# Search
Source: https://docs.law4devs.eu/sdks/python/search
Full-text search across EU frameworks with the Law4Devs Python SDK.
## Overview
The search endpoint performs full-text search across articles, recitals, and requirements. Results include a `match_context` snippet showing where the query matched. Use filters to narrow by framework or content type.
***
## Methods
### `query()`
Run a full-text search and return a page of results.
```python theme={null}
results = client.search.query("data breach notification")
print(f"Found {len(results.data)} results")
for r in results.data:
print(f" [{r.type}] {r.framework_slug} — {r.match_context[:80]}...")
```
**Parameters**
| Parameter | Type | Default | Description |
| ------------- | ----- | -------- | -------------------------------------------- |
| `q` | `str` | required | Search query string |
| `framework` | `str` | `None` | Limit to a specific framework slug |
| `result_type` | `str` | `None` | `"article"`, `"recital"`, or `"requirement"` |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[SearchResult]`
`page.meta.pages` is always `0` for search results. The search endpoint does not support traditional page-count pagination. Use the presence of `page.links.next` to detect whether more results exist.
***
## Model
### `SearchResult`
| Field | Type | Description |
| ------------------ | ------------- | --------------------------------------------------------- |
| `type` | `str` | Result type: `"article"`, `"recital"`, or `"requirement"` |
| `framework_slug` | `str` | Framework the result belongs to |
| `framework_name` | `str` | Human-readable framework name |
| `article_number` | `int \| None` | Article number (for article and requirement results) |
| `recital_number` | `int \| None` | Recital number (for recital results) |
| `paragraph_ref` | `str \| None` | Paragraph label (for requirement results) |
| `title` | `str \| None` | Article or section heading |
| `requirement_type` | `str \| None` | Requirement classification (requirement results only) |
| `match_context` | `str` | Snippet of matched text with surrounding context |
| `url` | `str` | Direct API URL to the matched resource |
***
## Examples
### Basic search
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
results = client.search.query("data breach notification")
for r in results.data:
print(f"[{r.type:12}] {r.framework_slug:8} {r.title or ''}")
print(f" ...{r.match_context}...")
print()
```
### Search within a specific framework
```python theme={null}
results = client.search.query("legitimate interest", framework="gdpr")
print(f"{len(results.data)} GDPR results for 'legitimate interest'")
for r in results.data:
ref = f"Art. {r.article_number}" if r.article_number else f"Recital {r.recital_number}"
print(f" {ref}: {r.match_context[:80]}")
```
### Filter by result type
```python theme={null}
# Only articles
articles = client.search.query("incident response", result_type="article")
# Only requirements
reqs = client.search.query("incident response", result_type="requirement")
print(f"Articles: {len(articles.data)}, Requirements: {len(reqs.data)}")
```
### Search across multiple frameworks manually
```python theme={null}
frameworks_to_search = ["gdpr", "nis2", "cra"]
query = "vulnerability disclosure"
all_results = []
for fw in frameworks_to_search:
page = client.search.query(query, framework=fw)
all_results.extend(page.data)
print(f"'{query}' found in {len(all_results)} places across {len(frameworks_to_search)} frameworks")
```
### Paginate through search results
```python theme={null}
query = "security measures"
page = client.search.query(query, per_page=50)
all_results = list(page.data)
# meta.pages is always 0 for search — check links.next instead
while page.links.next:
page = client.search.query(query, page=page.meta.page + 1, per_page=50)
all_results.extend(page.data)
print(f"Total results collected: {len(all_results)}")
```
### Display results grouped by framework
```python theme={null}
from collections import defaultdict
results = client.search.query("encryption", per_page=100)
by_framework = defaultdict(list)
for r in results.data:
by_framework[r.framework_name].append(r)
for fw_name, matches in sorted(by_framework.items()):
print(f"\n{fw_name} ({len(matches)} matches):")
for r in matches:
ref = f"Art. {r.article_number}" if r.article_number else f"Recital {r.recital_number}"
print(f" {ref}: {r.match_context[:60]}...")
```
Combine `framework` and `result_type` filters to narrow results significantly. For example, `result_type="requirement"` limits results to machine-readable obligations, which is useful when building compliance checkers.
Search queries are matched against parsed text content. Very short queries (one or two characters) may produce unexpected results. Use at least three characters for meaningful searches.
# Tags
Source: https://docs.law4devs.eu/sdks/python/tags
List and inspect classification tags with the Law4Devs Python SDK.
## Overview
Tags are predefined classification labels applied to articles and requirements. They enable cross-framework filtering by topic — for example, finding all content related to `"data-breach"` across GDPR, NIS2, and the CRA.
***
## Methods
### `list()`
Returns a page of all tags.
```python theme={null}
page = client.tags.list()
print(f"Total tags: {page.meta.total}")
for tag in page.data:
print(f" {tag.slug}: {tag.name}")
```
**Parameters**
| Parameter | Type | Default | Description |
| ---------- | ----- | ------- | ---------------------- |
| `page` | `int` | `1` | Page number |
| `per_page` | `int` | `20` | Items per page (1–100) |
**Returns** `Page[Tag]`
***
### `get()`
Fetch a single tag by its slug.
```python theme={null}
tag = client.tags.get("data-breach")
print(tag.name) # "Data Breach"
print(tag.description) # "Articles relating to personal data breach..."
print(tag.keywords) # ["breach", "notification", "incident", ...]
print(tag.color) # "#e74c3c"
```
**Parameters**
| Parameter | Type | Description |
| --------- | ----- | ------------------------------ |
| `slug` | `str` | Tag slug, e.g. `"data-breach"` |
**Returns** `Tag`
***
### `iter()`
Iterate over all tags.
```python theme={null}
for tag in client.tags.iter():
print(f"{tag.slug}: {tag.name}")
```
**Returns** `Iterator[Tag]`
***
## Model
### `Tag`
| Field | Type | Description |
| ------------- | ------------- | ----------------------------------------- |
| `id` | `int` | Internal numeric ID |
| `slug` | `str` | URL-safe identifier, e.g. `"data-breach"` |
| `name` | `str` | Human-readable label |
| `description` | `str \| None` | What this tag covers |
| `keywords` | `list[str]` | Keywords used to auto-tag content |
| `color` | `str \| None` | Hex color code for UI display |
| `created_at` | `str` | ISO 8601 creation timestamp |
***
## Examples
### Print all tags with descriptions
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient()
for tag in client.tags.iter():
desc = tag.description or "(no description)"
print(f"{tag.name:25} {desc[:60]}")
```
### Look up a tag's keywords
```python theme={null}
tag = client.tags.get("consent")
print(f"Auto-tag keywords for '{tag.name}':")
for keyword in tag.keywords:
print(f" - {keyword}")
```
### Find all articles tagged with a given label
```python theme={null}
target_tag = "security"
matches = [
article
for article in client.articles.iter("nis2")
if target_tag in article.tags
]
print(f"{len(matches)} NIS2 articles tagged '{target_tag}':")
for a in matches:
print(f" Art. {a.article_number}: {a.title}")
```
### Build a tag index across all frameworks
```python theme={null}
tag_index = {} # tag_slug -> list of (framework, article_number)
for fw in client.frameworks.iter():
for article in client.articles.iter(fw.slug):
for tag_slug in article.tags:
tag_index.setdefault(tag_slug, []).append(
(fw.slug, article.article_number)
)
# Print most used tags
for slug, refs in sorted(tag_index.items(), key=lambda x: -len(x[1])):
print(f"{slug}: {len(refs)} articles")
```
Tag slugs are stable — safe to hardcode in application logic or store in databases as foreign keys.
# Annexes
Source: https://docs.law4devs.eu/sdks/rust/annexes
List and retrieve framework annexes with the Rust SDK.
## Methods
```rust theme={null}
client.annexes.list(framework_slug: &str, page: Option, per_page: Option)
-> Result, Law4DevsError>
client.annexes.get(framework_slug: &str, annex_number: &str)
-> Result
client.annexes.iter(framework_slug: &str, per_page: u32)
-> impl Stream- >
```
***
## List Annexes
```rust theme={null}
let page = client.annexes.list("cra", None, None).await?;
for a in &page.data {
println!("Annex {}: {}", a.annex_number, a.title);
}
```
***
## Get an Annex
```rust theme={null}
let annex = client.annexes.get("cra", "I").await?;
println!("{}", annex.content);
```
***
## Auto-Paginate
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.annexes.iter("cra", 20));
while let Some(result) = stream.next().await {
let a = result?;
println!("Annex {} — {}", a.annex_number, a.title);
}
```
***
## `AnnexSummary` Fields
| Field | Type | Description |
| ----------------- | ---------------- | ----------------------------- |
| `id` | `u32` | Internal ID |
| `framework_slug` | `String` | Parent framework slug |
| `annex_number` | `String` | Annex identifier (e.g. `"I"`) |
| `title` | `String` | Annex title |
| `content_summary` | `Option` | Brief summary |
## `Annex` Additional Fields
| Field | Type | Description |
| --------- | -------- | --------------- |
| `content` | `String` | Full annex text |
# Articles
Source: https://docs.law4devs.eu/sdks/rust/articles
List, retrieve, and iterate articles with the Rust SDK.
## Methods
```rust theme={null}
// List articles for a framework
client.articles.list(framework_slug: &str, page: Option, per_page: Option)
-> Result, Law4DevsError>
// Get a specific article
client.articles.get(framework_slug: &str, article_number: &str)
-> Result
// Get related articles
client.articles.related(framework_slug: &str, article_number: &str, page: Option, per_page: Option)
-> Result, Law4DevsError>
// Auto-paginate all articles
client.articles.iter(framework_slug: &str, per_page: u32)
-> impl Stream
- >
```
***
## List Articles
```rust theme={null}
let page = client.articles.list("gdpr", None, None).await?;
for a in &page.data {
println!("Art. {}: {}", a.article_number, a.title);
}
```
***
## Get an Article
```rust theme={null}
let article = client.articles.get("gdpr", "5").await?;
println!("{}", article.content);
for para in &article.paragraphs {
println!("[{}] {}", para.paragraph_ref, para.content);
}
```
***
## Related Articles
```rust theme={null}
let related = client.articles.related("gdpr", "5", None, None).await?;
for a in &related.data {
println!("{}: {}", a.article_number, a.title);
}
```
***
## Auto-Paginate
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.articles.iter("nis2", 20));
while let Some(result) = stream.next().await {
let a = result?;
println!("Art. {} — {}", a.article_number, a.title);
}
```
***
## `ArticleSummary` Fields
| Field | Type | Description |
| ----------------- | ---------- | --------------------------- |
| `id` | `u32` | Internal ID |
| `framework_slug` | `String` | Parent framework slug |
| `article_number` | `String` | Article number (e.g. `"5"`) |
| `title` | `String` | Article title |
| `position` | `u32` | Order within the framework |
| `paragraph_count` | `u32` | Number of paragraphs |
| `tags` | `Vec` | Associated compliance tags |
***
## `Article` Additional Fields
| Field | Type | Description |
| ------------ | ----------------------- | --------------------- |
| `content` | `String` | Full article text |
| `paragraphs` | `Vec` | Individual paragraphs |
# Compliance Deadlines
Source: https://docs.law4devs.eu/sdks/rust/compliance
List and iterate compliance deadlines with the Rust SDK.
## Methods
```rust theme={null}
client.compliance.deadlines(framework_slug: Option<&str>, page: Option, per_page: Option)
-> Result, Law4DevsError>
client.compliance.iter_deadlines(framework_slug: Option<&str>, per_page: u32)
-> impl Stream
- >
```
***
## List Deadlines
```rust theme={null}
// All deadlines
let page = client.compliance.deadlines(None, None, None).await?;
// Deadlines for NIS2
let page = client.compliance.deadlines(Some("nis2"), None, None).await?;
for d in &page.data {
println!("{} — {}", d.deadline, d.title);
}
```
***
## Auto-Paginate
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.compliance.iter_deadlines(Some("nis2"), 20));
while let Some(result) = stream.next().await {
let d = result?;
println!("{} — {}", d.deadline, d.title);
}
```
***
## `ComplianceDeadline` Fields
| Field | Type | Description |
| ------------------- | ---------------- | --------------------- |
| `id` | `u32` | Internal ID |
| `framework_slug` | `String` | Parent framework slug |
| `title` | `String` | Deadline title |
| `description` | `Option` | Detailed description |
| `deadline` | `String` | ISO 8601 date |
| `deadline_type` | `Option` | Type of deadline |
| `article_reference` | `Option` | Source article |
# Error Handling
Source: https://docs.law4devs.eu/sdks/rust/error-handling
Handle API errors gracefully with the Rust SDK.
## Error Enum
```
Law4DevsError
├── NotFound(String) — 404 Not Found
├── Validation(String) — 400 Bad Request
├── RateLimit — 429 Too Many Requests
├── Server(u16, String) — 5xx Server Error
├── Network(reqwest::Error)— Network/timeout failure
└── Parse(serde_json::Error)— JSON deserialization failure
```
`Law4DevsError` implements `std::error::Error` and `Display` via `thiserror`.
***
## Matching Errors
```rust theme={null}
use law4devs::{Law4DevsClient, Law4DevsError};
match client.frameworks.get("nonexistent").await {
Ok(fw) => println!("{}", fw.name),
Err(Law4DevsError::NotFound(msg)) => eprintln!("404: {msg}"),
Err(Law4DevsError::Validation(msg)) => eprintln!("400: {msg}"),
Err(Law4DevsError::RateLimit) => eprintln!("429: rate limited"),
Err(Law4DevsError::Server(code, msg)) => eprintln!("{code}: {msg}"),
Err(Law4DevsError::Network(e)) => eprintln!("network: {e}"),
Err(e) => eprintln!("other: {e}"),
}
```
***
## With `?` Propagation
```rust theme={null}
async fn get_cra_articles(client: &Law4DevsClient) -> Result, Law4DevsError> {
let page = client.articles.list("cra", None, Some(50)).await?;
Ok(page.data)
}
```
***
## Retry Behavior
The SDK automatically retries `429` and `5xx` responses with exponential backoff:
| Attempt | Delay |
| --------- | --------- |
| 1st retry | 1 second |
| 2nd retry | 2 seconds |
| 3rd retry | 4 seconds |
`404` and `400` are thrown immediately without retrying.
***
## Safe Get Pattern
```rust theme={null}
async fn safe_get(client: &Law4DevsClient, slug: &str) -> Option {
match client.frameworks.get(slug).await {
Ok(fw) => Some(fw),
Err(Law4DevsError::NotFound(_)) => None,
Err(e) => { eprintln!("Error: {e}"); None }
}
}
```
***
## Streams and Errors
Each item in an `iter()` stream is a `Result`. A single failed page stops the stream:
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.articles.iter("gdpr", 20));
while let Some(result) = stream.next().await {
match result {
Ok(a) => println!("{}", a.title),
Err(e) => { eprintln!("Stream error: {e}"); break; }
}
}
```
# Frameworks
Source: https://docs.law4devs.eu/sdks/rust/frameworks
List and retrieve EU regulatory frameworks with the Rust SDK.
## Methods
```rust theme={null}
// List frameworks (paginated)
client.frameworks.list(page: Option, per_page: Option)
-> Result, Law4DevsError>
// Get a framework by slug
client.frameworks.get(slug: &str)
-> Result
// Auto-paginate all frameworks
client.frameworks.iter(per_page: u32)
-> impl Stream
- >
```
***
## List Frameworks
```rust theme={null}
let page = client.frameworks.list(None, None).await?;
println!("{} frameworks", page.meta.total);
for fw in &page.data {
println!("{} — {}", fw.slug, fw.name);
}
```
With pagination:
```rust theme={null}
let page = client.frameworks.list(Some(1), Some(10)).await?;
```
***
## Get a Framework
```rust theme={null}
let gdpr = client.frameworks.get("gdpr").await?;
println!("{}: {} articles", gdpr.name, gdpr.article_count);
```
***
## Auto-Paginate
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.frameworks.iter(20));
while let Some(result) = stream.next().await {
let fw = result?;
println!("{}", fw.name);
}
```
***
## `Framework` Fields
| Field | Type | Description |
| --------------- | ---------------- | --------------------------------- |
| `id` | `u32` | Internal ID |
| `slug` | `String` | URL-safe identifier (e.g. `gdpr`) |
| `name` | `String` | Full name |
| `short_name` | `String` | Abbreviated name |
| `celex_number` | `String` | EUR-Lex CELEX number |
| `description` | `Option` | Short description |
| `is_active` | `bool` | Whether actively maintained |
| `status` | `String` | `active` or `superseded` |
| `article_count` | `u32` | Number of scraped articles |
| `recital_count` | `u32` | Number of scraped recitals |
***
## `FrameworkDetail` Additional Fields
| Field | Type | Description |
| ------------------- | --------------- | ------------------------------- |
| `eurlex_url` | `String` | Link to original EUR-Lex source |
| `requirement_count` | `u32` | Total requirements |
| `annex_count` | `u32` | Total annexes |
| `tag_count` | `u32` | Number of associated tags |
| `coverage` | `Option` | Scrape coverage statistics |
# Installation
Source: https://docs.law4devs.eu/sdks/rust/installation
Install the Law4Devs Rust SDK and make your first API call.
## Requirements
* Rust 1.85+
* Tokio async runtime
## Install
Add to your `Cargo.toml`:
```toml theme={null}
[dependencies]
law4devs = "0.1"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
```
Or use `cargo add`:
```bash theme={null}
cargo add law4devs
cargo add tokio --features full
cargo add futures
```
## First Call
```rust theme={null}
use law4devs::Law4DevsClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let client = Law4DevsClient::new();
let page = client.frameworks.list(None, None).await?;
println!("{} frameworks", page.meta.total);
Ok(())
}
```
## Configuration
```rust theme={null}
use std::time::Duration;
use law4devs::Law4DevsClient;
let client = Law4DevsClient::builder()
.api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default())
.base_url("https://api.law4devs.eu/v1") // default
.timeout(Duration::from_secs(30)) // default
.max_retries(3) // retries on 429/5xx
.build();
```
| Option | Type | Default | Description |
| ------------- | ---------- | -------------- | --------------------------------------------------- |
| `api_key` | `&str` | `None` | Your API key (required for authenticated endpoints) |
| `base_url` | `&str` | Production URL | Override for dev/staging environment |
| `timeout` | `Duration` | 30 seconds | Per-request HTTP timeout |
| `max_retries` | `u32` | `3` | Retries on 429/5xx with exponential backoff |
## Environment Variable
```bash theme={null}
export LAW4DEVS_API_KEY="your-key"
```
```rust theme={null}
let client = Law4DevsClient::builder()
.api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default())
.build();
```
Never hardcode your API key in source code or commit it to version control.
## Verify the Install
```rust theme={null}
// src/main.rs
use law4devs::Law4DevsClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let client = Law4DevsClient::builder()
.api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default())
.build();
let page = client.frameworks.list(None, None).await?;
println!("{} frameworks", page.meta.total);
Ok(())
}
```
```bash theme={null}
LAW4DEVS_API_KEY=your-key cargo run
# → 19 frameworks
```
# Pagination
Source: https://docs.law4devs.eu/sdks/rust/pagination
Understand manual and automatic pagination in the Rust SDK.
## Overview
Every `list()` / `deadlines()` / `query()` call returns a `Page`. The `iter()` / `iter_deadlines()` methods return a lazy `Stream` that auto-paginates across all pages.
***
## The `Page` Object
```rust theme={null}
let page = client.frameworks.list(Some(1), Some(20)).await?;
page.data // Vec — items on this page
page.meta.total // u32 — total items across all pages
page.meta.page // u32 — current page number
page.meta.per_page // u32 — items per page
page.meta.pages // u32 — total number of pages
page.links.next // Option — URL of next page
page.links.prev // Option — URL of previous page
page.has_next() // bool — true if more pages exist
```
**`PageMeta` fields**
| Field | Type | Description |
| ---------- | ----- | -------------- |
| `total` | `u32` | Total items |
| `page` | `u32` | Current page |
| `per_page` | `u32` | Items per page |
| `pages` | `u32` | Total pages |
***
## Manual Pagination
```rust theme={null}
let mut page_num = 1u32;
loop {
let page = client.frameworks.list(Some(page_num), Some(20)).await?;
for fw in &page.data {
println!("{}", fw.name);
}
if !page.has_next() { break; }
page_num += 1;
}
```
***
## Automatic Pagination with `iter()`
`iter()` returns a `Stream` — it fetches the next page only when the current page is exhausted. Use `Box::pin` to make it `Unpin` for `.next().await`:
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.frameworks.iter(20));
while let Some(result) = stream.next().await {
let fw = result?;
println!("{}", fw.name);
}
```
With a custom page size:
```rust theme={null}
let mut stream = Box::pin(client.articles.iter("gdpr", 50));
while let Some(result) = stream.next().await {
let a = result?;
println!("{}", a.title);
}
```
***
## Early Stopping
```rust theme={null}
let mut stream = Box::pin(client.frameworks.iter(20));
while let Some(result) = stream.next().await {
let fw = result?;
if fw.slug == "gdpr" {
println!("Found GDPR");
break;
}
}
```
***
## Progress Tracking
```rust theme={null}
let total = client.frameworks.list(None, None).await?.meta.total;
let mut count = 0u32;
let mut stream = Box::pin(client.frameworks.iter(20));
while let Some(result) = stream.next().await {
let fw = result?;
count += 1;
println!("[{count}/{total}] {}", fw.name);
}
```
# Quick Start
Source: https://docs.law4devs.eu/sdks/rust/quickstart
Make your first Law4Devs API call with Rust in under 60 seconds.
## Install
```toml theme={null}
[dependencies]
law4devs = "0.1"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
```
## Basic Usage
```rust theme={null}
use futures::StreamExt;
use law4devs::Law4DevsClient;
#[tokio::main]
async fn main() -> Result<(), Box> {
let client = Law4DevsClient::builder()
.api_key(std::env::var("LAW4DEVS_API_KEY").unwrap_or_default())
.build();
// List all frameworks
let page = client.frameworks.list(None, None).await?;
for fw in &page.data {
println!("{} — {}", fw.slug, fw.name);
}
// List first 5 CRA articles
let articles = client.articles.list("cra", None, Some(5)).await?;
for a in &articles.data {
println!("Art. {}: {}", a.article_number, a.title);
}
// Auto-paginate all GDPR articles
let mut stream = Box::pin(client.articles.iter("gdpr", 20));
while let Some(result) = stream.next().await {
let a = result?;
println!("{} {}", a.article_number, a.title);
}
// Search across all frameworks
let results = client
.search
.query("data breach notification", None, None, None, None)
.await?;
println!("Found {} results", results.meta.total);
Ok(())
}
```
## Next Steps
# Recitals
Source: https://docs.law4devs.eu/sdks/rust/recitals
List and retrieve recitals with the Rust SDK.
## Methods
```rust theme={null}
client.recitals.list(framework_slug: &str, page: Option, per_page: Option)
-> Result, Law4DevsError>
client.recitals.get(framework_slug: &str, recital_number: &str)
-> Result
client.recitals.iter(framework_slug: &str, per_page: u32)
-> impl Stream
- >
```
***
## List Recitals
```rust theme={null}
let page = client.recitals.list("gdpr", None, None).await?;
for r in &page.data {
println!("Recital {}: {}...", r.recital_number, &r.content[..60]);
}
```
***
## Get a Recital
```rust theme={null}
let recital = client.recitals.get("gdpr", "1").await?;
println!("{}", recital.content);
```
***
## Auto-Paginate
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.recitals.iter("gdpr", 20));
while let Some(result) = stream.next().await {
let r = result?;
println!("Recital {}", r.recital_number);
}
```
***
## `Recital` Fields
| Field | Type | Description |
| ---------------- | -------- | -------------------------- |
| `id` | `u32` | Internal ID |
| `framework_slug` | `String` | Parent framework slug |
| `recital_number` | `String` | Recital number |
| `content` | `String` | Full recital text |
| `position` | `u32` | Order within the framework |
# Requirements
Source: https://docs.law4devs.eu/sdks/rust/requirements
List and iterate compliance requirements with the Rust SDK.
## Methods
```rust theme={null}
client.requirements.list(framework_slug: Option<&str>, page: Option, per_page: Option)
-> Result, Law4DevsError>
client.requirements.iter(framework_slug: Option<&str>, per_page: u32)
-> impl Stream
- >
```
***
## List Requirements
```rust theme={null}
// All requirements
let page = client.requirements.list(None, None, None).await?;
// Requirements for a specific framework
let page = client.requirements.list(Some("cra"), None, None).await?;
```
***
## Auto-Paginate
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.requirements.iter(Some("cra"), 20));
while let Some(result) = stream.next().await {
let r = result?;
println!("[{}] {}", r.requirement_type, &r.requirement_text[..60.min(r.requirement_text.len())]);
}
```
***
## `Requirement` Fields
| Field | Type | Description |
| ------------------- | ---------------- | ---------------------------- |
| `id` | `u32` | Internal ID |
| `framework_slug` | `String` | Parent framework slug |
| `requirement_type` | `String` | Type/category of requirement |
| `requirement_text` | `String` | Full requirement text |
| `article_reference` | `Option` | Source article number |
| `stakeholder_type` | `Option` | Target stakeholder |
# Search
Source: https://docs.law4devs.eu/sdks/rust/search
Search across all EU frameworks with the Rust SDK.
## Methods
```rust theme={null}
client.search.query(
q: &str,
framework: Option<&str>,
result_type: Option<&str>,
page: Option,
per_page: Option,
) -> Result, Law4DevsError>
client.search.iter(
q: &str,
framework: Option<&str>,
result_type: Option<&str>,
per_page: u32,
) -> impl Stream
- >
```
***
## Basic Search
```rust theme={null}
let results = client
.search
.query("incident notification 72 hours", None, None, None, None)
.await?;
println!("Found {} results", results.meta.total);
for r in &results.data {
println!("[{}] {} {}: {}", r.framework_slug.to_uppercase(), r.result_type, r.number, r.title);
}
```
***
## Filtered Search
```rust theme={null}
// Search within a specific framework
let results = client
.search
.query("data breach", Some("gdpr"), None, None, Some(20))
.await?;
// Filter by result type
let results = client
.search
.query("encryption", Some("gdpr"), Some("article"), None, None)
.await?;
```
***
## Auto-Paginate Results
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.search.iter("vulnerability disclosure", None, None, 20));
while let Some(result) = stream.next().await {
let r = result?;
println!("[{}] {}", r.framework_slug, r.title);
}
```
***
## `SearchResult` Fields
| Field | Type | Description |
| ---------------- | ---------------- | -------------------------------------- |
| `id` | `u32` | Internal ID |
| `framework_slug` | `String` | Source framework |
| `result_type` | `String` | `article`, `recital`, or `requirement` |
| `number` | `String` | Article/recital/requirement number |
| `title` | `String` | Title or first line |
| `snippet` | `Option` | Matched text excerpt |
| `score` | `f64` | Relevance score |
# Tags
Source: https://docs.law4devs.eu/sdks/rust/tags
List and retrieve compliance tags with the Rust SDK.
## Methods
```rust theme={null}
client.tags.list(page: Option, per_page: Option)
-> Result, Law4DevsError>
client.tags.get(slug: &str)
-> Result
client.tags.iter(per_page: u32)
-> impl Stream
- >
```
***
## List Tags
```rust theme={null}
let page = client.tags.list(None, None).await?;
for tag in &page.data {
println!("{} — {} articles", tag.name, tag.article_count);
}
```
***
## Get a Tag
```rust theme={null}
let tag = client.tags.get("data-protection").await?;
println!("{}: {} frameworks", tag.name, tag.framework_count);
```
***
## Auto-Paginate
```rust theme={null}
use futures::StreamExt;
let mut stream = Box::pin(client.tags.iter(50));
while let Some(result) = stream.next().await {
let t = result?;
println!("{}", t.slug);
}
```
***
## `Tag` Fields
| Field | Type | Description |
| ----------------- | ---------------- | ------------------------- |
| `id` | `u32` | Internal ID |
| `slug` | `String` | URL-safe identifier |
| `name` | `String` | Display name |
| `description` | `Option` | Tag description |
| `framework_count` | `u32` | Frameworks using this tag |
| `article_count` | `u32` | Articles with this tag |
# Annexes
Source: https://docs.law4devs.eu/sdks/typescript/annexes
List, retrieve, and iterate framework annexes with the Law4Devs TypeScript SDK.
## Overview
Annexes are supplementary parts of EU legislation, often containing technical specifications, lists, or tables referenced within the articles. Each annex belongs to one framework and is identified by its annex number or letter (e.g. `"I"`, `"II"`, `"A"`).
***
## Methods
### `list()`
Returns a page of annex summaries for a framework.
```typescript theme={null}
const page = await client.annexes.list('cra', { page: 1, perPage: 20 });
console.log(`CRA has ${page.meta.total} annexes`);
for (const annex of page.data) {
console.log(`Annex ${annex.annexNumber}: ${annex.title}`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | -------- | ---------------------------- |
| `frameworkSlug` | `string` | required | Framework slug, e.g. `"cra"` |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
***
### `get()`
Fetch the full content of a single annex.
```typescript theme={null}
const annex = await client.annexes.get('cra', 'I');
console.log(`Annex ${annex.annexNumber}: ${annex.title}`);
console.log(annex.content);
```
**Parameters**
| Parameter | Type | Description |
| --------------- | -------- | ------------------------------------------- |
| `frameworkSlug` | `string` | Framework slug |
| `annexNumber` | `string` | Annex identifier, e.g. `"I"`, `"II"`, `"A"` |
**Returns** `Promise`
***
### `iter()`
Iterate over all annexes in a framework.
```typescript theme={null}
for await (const annex of client.annexes.iter('cra')) {
console.log(`Annex ${annex.annexNumber}: ${annex.title}`);
}
```
**Parameters** — same options as `list()` (except `page`, which is managed internally).
**Returns** `AsyncGenerator`
***
## Models
### `AnnexSummary`
Returned by `list()` and `iter()`.
| Field | Type | Description |
| --------------- | ---------------- | ------------------------------------------- |
| `id` | `number` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `annexNumber` | `string` | Annex identifier, e.g. `"I"`, `"II"`, `"A"` |
| `title` | `string \| null` | Annex title |
| `position` | `number` | Display order within the framework |
### `Annex`
Returned by `get()`. Extends `AnnexSummary` with:
| Field | Type | Description |
| --------- | ---------------- | --------------- |
| `content` | `string \| null` | Full annex text |
***
## Example: enumerate all CRA annexes
```typescript theme={null}
const annexTitles: string[] = [];
for await (const annex of client.annexes.iter('cra')) {
annexTitles.push(`Annex ${annex.annexNumber}: ${annex.title ?? '(no title)'}`);
}
console.log('CRA Annexes:');
for (const title of annexTitles) {
console.log(` - ${title}`);
}
```
# Articles
Source: https://docs.law4devs.eu/sdks/typescript/articles
List, retrieve, and iterate framework articles with the Law4Devs TypeScript SDK.
## Overview
Articles are the primary normative units of each EU regulation. Each article belongs to one framework and may contain multiple numbered paragraphs. Articles can also be tagged and cross-referenced to related articles.
***
## Methods
### `list()`
Returns a page of article summaries for a framework.
```typescript theme={null}
const page = await client.articles.list('gdpr', { page: 1, perPage: 20 });
console.log(`GDPR has ${page.meta.total} articles`);
for (const article of page.data) {
console.log(`Art. ${article.articleNumber}: ${article.title} (${article.paragraphCount} paragraphs)`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | -------- | ----------------------------- |
| `frameworkSlug` | `string` | required | Framework slug, e.g. `"gdpr"` |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
***
### `get()`
Fetch the full content of a single article including all paragraphs.
```typescript theme={null}
const article = await client.articles.get('gdpr', 5);
console.log(`Art. ${article.articleNumber}: ${article.title}`);
console.log(article.content);
for (const para of article.paragraphs) {
console.log(` [${para.paragraphRef}] ${para.content}`);
}
```
**Parameters**
| Parameter | Type | Description |
| --------------- | -------- | -------------- |
| `frameworkSlug` | `string` | Framework slug |
| `articleNumber` | `number` | Article number |
**Returns** `Promise`
***
### `related()`
Fetch articles related to a given article (cross-references and thematic links).
```typescript theme={null}
const related = await client.articles.related('gdpr', 5);
console.log(`${related.meta.total} articles related to GDPR Art. 5`);
for (const article of related.data) {
console.log(` Art. ${article.articleNumber}: ${article.title}`);
}
```
**Parameters**
| Parameter | Type | Description |
| --------------- | -------- | -------------- |
| `frameworkSlug` | `string` | Framework slug |
| `articleNumber` | `number` | Article number |
**Returns** `Promise>`
***
### `iter()`
Iterate over all articles in a framework without managing pagination.
```typescript theme={null}
for await (const article of client.articles.iter('gdpr')) {
const tagList = article.tags.map(t => t.slug).join(', ');
console.log(`Art. ${article.articleNumber}: ${article.title} [${tagList}]`);
}
```
**Parameters** — same options as `list()` (except `frameworkSlug` is the first positional argument and `page` is managed internally).
**Returns** `AsyncGenerator`
***
## Models
### `ArticleSummary`
Returned by `list()`, `related()`, and `iter()`.
| Field | Type | Description |
| ---------------- | ---------------- | ---------------------------------- |
| `id` | `number` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `articleNumber` | `number` | Article number |
| `title` | `string \| null` | Article title |
| `position` | `number` | Display order within the framework |
| `paragraphCount` | `number` | Number of paragraphs |
| `tags` | `Tag[]` | Tags applied to the article |
### `Article`
Returned by `get()`. Extends `ArticleSummary` with:
| Field | Type | Description |
| ------------ | -------------------- | ------------------------- |
| `content` | `string \| null` | Full article text |
| `paragraphs` | `ArticleParagraph[]` | Structured paragraph list |
### `ArticleParagraph`
| Field | Type | Description |
| -------------- | -------- | --------------------------------------- |
| `paragraphRef` | `string` | Paragraph reference, e.g. `"1"`, `"2a"` |
| `content` | `string` | Paragraph text |
| `position` | `number` | Display order within the article |
***
## Example: find all articles tagged "data-breach"
```typescript theme={null}
const breachArticles: string[] = [];
for await (const article of client.articles.iter('gdpr')) {
const hasTag = article.tags.some(t => t.slug === 'data-breach');
if (hasTag) {
breachArticles.push(`Art. ${article.articleNumber}: ${article.title}`);
}
}
console.log('Data-breach articles:', breachArticles);
```
# Compliance Deadlines
Source: https://docs.law4devs.eu/sdks/typescript/compliance
Query and iterate compliance deadlines with the Law4Devs TypeScript SDK.
## Overview
Compliance deadlines are specific dates by which organisations must have completed certain obligations under a regulation. They are extracted from articles and classified by type (e.g. `"transposition"`, `"application"`, `"notification"`).
***
## Methods
### `deadlines()`
Returns a page of compliance deadlines, optionally filtered to a single framework.
```typescript theme={null}
const page = await client.compliance.deadlines({ frameworkSlug: 'nis2' });
console.log(`NIS2 has ${page.meta.total} compliance deadlines`);
for (const deadline of page.data) {
const date = deadline.deadlineDate ?? 'no fixed date';
console.log(`[${deadline.deadlineType}] ${date}: ${deadline.description}`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | ------- | ------------------------------ |
| `frameworkSlug` | `string` | — | Filter to a specific framework |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
***
### `iterDeadlines()`
Iterate over all compliance deadlines without managing pagination.
```typescript theme={null}
for await (const deadline of client.compliance.iterDeadlines({ frameworkSlug: 'nis2' })) {
const date = deadline.deadlineDate ?? 'ongoing';
const ref = deadline.paragraphRef ? ` §${deadline.paragraphRef}` : '';
console.log(`Art. ${deadline.articleNumber}${ref} — ${deadline.deadlineType}: ${date}`);
console.log(` ${deadline.description}`);
}
```
The method is `iterDeadlines()`, not `iter()`. This is intentional — the compliance resource covers deadlines across multiple frameworks rather than scoping to a single one.
**Parameters** — same options as `deadlines()` (except `page`, which is managed internally).
**Returns** `AsyncGenerator`
***
## Model
### `ComplianceDeadline`
| Field | Type | Description |
| --------------- | ---------------- | ------------------------------------------------------------------- |
| `id` | `number` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `articleNumber` | `number` | Source article number |
| `paragraphRef` | `string \| null` | Source paragraph reference |
| `deadlineDate` | `string \| null` | ISO 8601 deadline date, or `null` if ongoing |
| `deadlineType` | `string` | Category, e.g. `"transposition"`, `"application"`, `"notification"` |
| `description` | `string` | Human-readable description of the obligation |
***
## Example: build a deadline calendar
```typescript theme={null}
interface CalendarEntry {
date: string;
framework: string;
type: string;
description: string;
}
const calendar: CalendarEntry[] = [];
// Collect deadlines for two frameworks in parallel
const [nis2Page, craPage] = await Promise.all([
client.compliance.deadlines({ frameworkSlug: 'nis2', perPage: 100 }),
client.compliance.deadlines({ frameworkSlug: 'cra', perPage: 100 }),
]);
for (const deadline of [...nis2Page.data, ...craPage.data]) {
if (deadline.deadlineDate) {
calendar.push({
date: deadline.deadlineDate,
framework: deadline.frameworkSlug.toUpperCase(),
type: deadline.deadlineType,
description: deadline.description,
});
}
}
calendar.sort((a, b) => a.date.localeCompare(b.date));
for (const entry of calendar) {
console.log(`${entry.date} [${entry.framework}] ${entry.type}: ${entry.description}`);
}
```
To get deadlines across all frameworks at once, call `iterDeadlines()` without a `frameworkSlug` filter.
# Error Handling
Source: https://docs.law4devs.eu/sdks/typescript/error-handling
Handle API errors gracefully with the Law4Devs TypeScript SDK.
## Exception hierarchy
All errors thrown by the SDK extend `Law4DevsError`, which itself extends the built-in `Error`. Use `instanceof` checks to handle specific error types.
```
Law4DevsError
├── NotFoundError (HTTP 404)
├── ValidationError (HTTP 422)
├── RateLimitError (HTTP 429)
└── ServerError (HTTP 5xx)
```
Every error class exposes a `statusCode` property with the underlying HTTP status code.
```typescript theme={null}
import {
Law4DevsError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
} from 'law4devs';
```
***
## Basic try/catch
```typescript theme={null}
try {
const article = await client.articles.get('gdpr', 999);
console.log(article.title);
} catch (err) {
if (err instanceof NotFoundError) {
console.error(`Not found (${err.statusCode}): ${err.message}`);
} else if (err instanceof ValidationError) {
console.error(`Invalid request (${err.statusCode}): ${err.message}`);
} else if (err instanceof RateLimitError) {
console.error(`Rate limited (${err.statusCode}) — all retries exhausted`);
} else if (err instanceof ServerError) {
console.error(`Server error (${err.statusCode}) — all retries exhausted`);
} else if (err instanceof Law4DevsError) {
console.error(`API error (${err.statusCode}): ${err.message}`);
} else {
throw err; // network error or unexpected — re-throw
}
}
```
***
## Auto-retry behaviour
The SDK automatically retries on `RateLimitError` (429) and `ServerError` (5xx) using exponential backoff with jitter. Only after all retries are exhausted is the error thrown to your code.
```typescript theme={null}
// Configure retry behaviour at construction time
const client = new Law4DevsClient({
maxRetries: 3, // default: 3 — set to 0 to disable retries
timeout: 30000, // ms — each individual attempt has this timeout
});
```
Timeouts are enforced via `AbortController`, so they apply per-attempt (not across all retries combined).
`NotFoundError` and `ValidationError` are never retried — they indicate client-side issues that will not resolve on retry.
***
## TypeScript narrowing with a typed helper
Define a `safeGet` helper to return a typed result or a structured error without propagating exceptions:
```typescript theme={null}
import { Law4DevsClient, NotFoundError, Law4DevsError, Article } from 'law4devs';
type Result =
| { ok: true; value: T }
| { ok: false; error: Law4DevsError };
async function safeGet(fn: () => Promise): Promise> {
try {
return { ok: true, value: await fn() };
} catch (err) {
if (err instanceof Law4DevsError) {
return { ok: false, error: err };
}
throw err; // unexpected — re-throw
}
}
// Usage
const result = await safeGet(() => client.articles.get('gdpr', 5));
if (result.ok) {
console.log(result.value.title); // TypeScript knows result.value is Article
} else {
if (result.error instanceof NotFoundError) {
console.log('Article does not exist');
} else {
console.error(`Unexpected error: ${result.error.message}`);
}
}
```
***
## Handling errors inside `for await`
Errors thrown during `iter()` or `iterDeadlines()` are propagated out of the `for await` loop. Wrap the loop in try/catch to handle them:
```typescript theme={null}
try {
for await (const article of client.articles.iter('gdpr')) {
console.log(article.title);
}
} catch (err) {
if (err instanceof RateLimitError) {
console.error('Rate limit hit during iteration — consider reducing perPage or adding delays');
} else {
throw err;
}
}
```
***
## Error reference
| Class | HTTP Status | When thrown | Retried? |
| ----------------- | ----------- | --------------------------------- | -------- |
| `NotFoundError` | 404 | Requested resource does not exist | No |
| `ValidationError` | 422 | Invalid parameters or query | No |
| `RateLimitError` | 429 | Too many requests | Yes |
| `ServerError` | 5xx | Server-side error | Yes |
| `Law4DevsError` | any | Base class — catch-all | Depends |
After all retries are exhausted, `RateLimitError` and `ServerError` are thrown normally. Always include these in your catch blocks for production code.
# Frameworks
Source: https://docs.law4devs.eu/sdks/typescript/frameworks
List, retrieve, and iterate EU regulatory frameworks with the Law4Devs TypeScript SDK.
## Overview
Frameworks are the top-level objects in Law4Devs. Each framework represents one EU regulatory act (e.g. GDPR, NIS2, CRA) and acts as a namespace for articles, recitals, requirements, and annexes.
***
## Methods
### `list()`
Returns a page of framework summaries.
```typescript theme={null}
const page = await client.frameworks.list({ page: 1, perPage: 20 });
console.log(`${page.meta.total} frameworks across ${page.meta.pages} pages`);
for (const fw of page.data) {
console.log(`${fw.slug}: ${fw.name} (${fw.articleCount} articles)`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| --------- | -------- | ------- | ---------------------- |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
***
### `get()`
Fetch the full detail of a single framework by its slug.
```typescript theme={null}
const gdpr = await client.frameworks.get('gdpr');
console.log(gdpr.name);
console.log(`Coverage: ${gdpr.coverage}%`);
console.log(`EUR-Lex: ${gdpr.eurLexUrl}`);
```
**Parameters**
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------------ |
| `slug` | `string` | Framework slug, e.g. `"gdpr"`, `"nis2"`, `"cra"` |
**Returns** `Promise`
***
### `iter()`
Iterate over all frameworks without managing pagination.
```typescript theme={null}
for await (const fw of client.frameworks.iter({ perPage: 50 })) {
console.log(`${fw.shortName} — last synced: ${fw.lastSyncedAt}`);
}
```
**Parameters** — same options as `list()` (except `page`, which is managed internally).
**Returns** `AsyncGenerator`
***
## Models
### `Framework`
Returned by `list()` and `iter()`.
| Field | Type | Description |
| ------------------ | ---------------- | -------------------------------------------- |
| `id` | `number` | Internal numeric ID |
| `slug` | `string` | URL-safe identifier, e.g. `"gdpr"` |
| `name` | `string` | Full official name |
| `shortName` | `string` | Short name, e.g. `"GDPR"` |
| `celexNumber` | `string \| null` | EUR-Lex CELEX identifier |
| `description` | `string \| null` | Plain-text summary |
| `isActive` | `boolean` | Whether the framework is published |
| `status` | `string` | `"active"`, `"superseded"`, or `"draft"` |
| `expectedArticles` | `number \| null` | Target article count for sync progress |
| `expectedRecitals` | `number \| null` | Target recital count for sync progress |
| `lastSyncedAt` | `string \| null` | ISO 8601 timestamp of last content sync |
| `createdAt` | `string` | ISO 8601 creation timestamp |
| `articleCount` | `number` | Number of articles currently in the database |
| `recitalCount` | `number` | Number of recitals currently in the database |
### `FrameworkDetail`
Returned by `get()`. Extends `Framework` with:
| Field | Type | Description |
| ------------------ | ---------------- | -------------------------------------- |
| `eurLexUrl` | `string \| null` | Direct link to EUR-Lex source document |
| `requirementCount` | `number` | Number of extracted requirements |
| `annexCount` | `number` | Number of annexes |
| `tagCount` | `number` | Number of tags applied |
| `coverage` | `number` | Sync coverage percentage (0–100) |
***
## Example: filter active frameworks
```typescript theme={null}
const activeFrameworks: string[] = [];
for await (const fw of client.frameworks.iter()) {
if (fw.isActive && fw.status === 'active') {
activeFrameworks.push(fw.slug);
}
}
console.log('Active frameworks:', activeFrameworks.join(', '));
```
NIS1 has `status: "superseded"` and remains browsable for historical reference. Filter by `status === "active"` if you only want current legislation.
# Installation
Source: https://docs.law4devs.eu/sdks/typescript/installation
Install the Law4Devs TypeScript SDK and make your first API call.
## Requirements
* **Node.js 18+** (uses the native `fetch` API — no polyfill needed)
* npm, yarn, or pnpm
The SDK uses the global `fetch` available in Node 18 and all modern browsers. No additional HTTP dependencies are required.
***
## Install
```bash theme={null}
npm install law4devs
```
```bash theme={null}
yarn add law4devs
```
```bash theme={null}
pnpm add law4devs
```
***
## First Call
```typescript theme={null}
import { Law4DevsClient } from 'law4devs';
const client = new Law4DevsClient({
apiKey: process.env.LAW4DEVS_API_KEY!,
});
const page = await client.frameworks.list();
console.log(`Found ${page.meta.total} EU regulatory frameworks`);
for (const fw of page.data) {
console.log(`${fw.shortName}: ${fw.name}`);
}
```
***
## Constructor Options
```typescript theme={null}
const client = new Law4DevsClient({
apiKey: process.env.LAW4DEVS_API_KEY!,
baseUrl: 'https://api.law4devs.eu/v1', // default
timeout: 30_000, // ms before request is aborted (default: 30000)
maxRetries: 3, // retries on 429/5xx (default: 3)
});
```
| Option | Type | Default | Description |
| ------------ | -------- | -------------- | ------------------------------- |
| `apiKey` | `string` | — | **Required.** Your API key |
| `baseUrl` | `string` | Production URL | Override for dev environment |
| `timeout` | `number` | `30000` | Request timeout in milliseconds |
| `maxRetries` | `number` | `3` | Retries on 429/5xx |
Never hardcode your API key in source code or commit it to version control. Always use environment variables.
Retries use exponential backoff. Timeouts are enforced via `AbortController` and work in both Node.js and browsers.
# Pagination
Source: https://docs.law4devs.eu/sdks/typescript/pagination
Manual pagination and auto-pagination with AsyncGenerator in the Law4Devs TypeScript SDK.
## The `Page` type
Every `list()` method returns a `Promise>`. The `Page` object has three properties:
```typescript theme={null}
interface Page {
data: T[]; // items on the current page
meta: PageMeta; // totals and current position
links: PageLinks; // pre-built URLs for adjacent pages
}
interface PageMeta {
total: number; // total items across all pages
page: number; // current page number (1-based)
perPage: number; // items per page
pages: number; // total number of pages
apiVersion: string; // API version string
}
interface PageLinks {
next: string | null; // URL of the next page, or null on the last page
prev: string | null; // URL of the previous page, or null on the first page
}
```
***
## Manual pagination
Use `links.next` to detect when there are more pages and increment `page` manually.
```typescript theme={null}
let page = 1;
let total = 0;
while (true) {
const result = await client.frameworks.list({ page, perPage: 10 });
total += result.data.length;
console.log(`Page ${result.meta.page}/${result.meta.pages}: ${result.data.length} items`);
if (!result.links.next) break; // no more pages
page++;
}
console.log(`Fetched ${total} frameworks in total`);
```
***
## Auto-pagination with `for await`
Every resource exposes an `iter()` method (or `iterDeadlines()` for compliance) that returns an `AsyncGenerator`. Pages are fetched lazily — only when the current batch is exhausted.
```typescript theme={null}
// Frameworks
for await (const fw of client.frameworks.iter()) {
console.log(fw.slug);
}
// Articles scoped to a framework
for await (const article of client.articles.iter('gdpr')) {
console.log(`Art. ${article.articleNumber}: ${article.title}`);
}
// Compliance deadlines
for await (const deadline of client.compliance.iterDeadlines({ frameworkSlug: 'nis2' })) {
console.log(deadline.description);
}
```
TypeScript infers the correct item type from the generator signature:
```typescript theme={null}
// article is typed as ArticleSummary — no cast needed
for await (const article of client.articles.iter('cra')) {
const tags: Tag[] = article.tags; // fully typed
}
```
***
## Early exit
Because `iter()` is a lazy `AsyncGenerator`, breaking out of the loop stops further fetches immediately.
```typescript theme={null}
// Find the first article tagged 'security' — stop as soon as found
let found: ArticleSummary | undefined;
for await (const article of client.articles.iter('nis2')) {
if (article.tags.some(t => t.slug === 'security')) {
found = article;
break; // no further pages are fetched
}
}
if (found) {
console.log(`First security article: Art. ${found.articleNumber}`);
}
```
***
## Controlling page size
Pass `perPage` to `iter()` or `list()` to tune the number of items fetched per HTTP request. Valid range is 1–100; default is 20.
```typescript theme={null}
// Fewer, larger pages — fewer HTTP requests
for await (const req of client.requirements.iter({
frameworkSlug: 'gdpr',
perPage: 100,
})) {
// process requirement
}
```
For large datasets, a higher `perPage` value reduces the number of HTTP round-trips. For interactive UIs where you want to display results quickly, a smaller `perPage` value returns the first items faster.
***
## Collecting all results into an array
```typescript theme={null}
async function collectAll(gen: AsyncGenerator): Promise {
const items: T[] = [];
for await (const item of gen) {
items.push(item);
}
return items;
}
const allFrameworks = await collectAll(client.frameworks.iter());
console.log(`${allFrameworks.length} total frameworks`);
```
Collecting all results into memory with a helper like `collectAll` is convenient but be mindful of response size. For large resources such as requirements or articles across all frameworks, stream and process items one-by-one inside the `for await` loop instead.
# Quick Start
Source: https://docs.law4devs.eu/sdks/typescript/quickstart
Practical examples to get up and running with the Law4Devs TypeScript SDK.
## Setup
```typescript theme={null}
import { Law4DevsClient } from 'law4devs';
const client = new Law4DevsClient();
```
***
## 1. List all frameworks
```typescript theme={null}
const page = await client.frameworks.list({ perPage: 50 });
console.log(`${page.meta.total} frameworks available`);
for (const fw of page.data) {
const label = fw.status === 'active' ? fw.shortName : `${fw.shortName} (${fw.status})`;
console.log(`${label}: ${fw.name}`);
}
```
***
## 2. Iterate all CRA articles
Use `for await` to walk through every article without managing pagination manually.
```typescript theme={null}
let count = 0;
for await (const article of client.articles.iter('cra')) {
console.log(`Art. ${article.articleNumber}: ${article.title}`);
count++;
}
console.log(`Iterated ${count} CRA articles`);
```
`iter()` fetches pages lazily. Break out of the loop early with `break` as soon as you have what you need — subsequent pages are never fetched.
***
## 3. Search with a framework filter
```typescript theme={null}
const results = await client.search.query('security requirements', {
framework: 'nis2',
resultType: 'article',
perPage: 10,
});
for (const result of results.data) {
console.log(`[${result.type}] Art. ${result.articleNumber}: ${result.title}`);
console.log(` ...${result.matchContext}...`);
}
```
***
## 4. Get compliance deadlines with `iterDeadlines()`
```typescript theme={null}
for await (const deadline of client.compliance.iterDeadlines({ frameworkSlug: 'nis2' })) {
const date = deadline.deadlineDate ?? 'ongoing';
console.log(`${deadline.deadlineType} — ${date}: ${deadline.description}`);
}
```
The compliance resource uses `iterDeadlines()` rather than `iter()` because it operates across multiple frameworks rather than within a single one.
***
## 5. Error handling
```typescript theme={null}
import { Law4DevsClient, NotFoundError, RateLimitError } from 'law4devs';
const client = new Law4DevsClient({ maxRetries: 3 });
try {
const article = await client.articles.get('gdpr', 999);
console.log(article.title);
} catch (err) {
if (err instanceof NotFoundError) {
console.error(`Article not found (HTTP ${err.statusCode})`);
} else if (err instanceof RateLimitError) {
console.error('Rate limit hit — the SDK retries automatically, but all retries were exhausted');
} else {
throw err; // re-throw unexpected errors
}
}
```
***
## Next Steps
# Recitals
Source: https://docs.law4devs.eu/sdks/typescript/recitals
List, retrieve, and iterate framework recitals with the Law4Devs TypeScript SDK.
## Overview
Recitals are the numbered "whereas" clauses that precede the operative articles of an EU regulation. They provide legislative context and interpretation guidance but are not themselves legally binding obligations. Each recital belongs to one framework.
***
## Methods
### `list()`
Returns a page of recital summaries for a framework.
```typescript theme={null}
const page = await client.recitals.list('gdpr', { page: 1, perPage: 20 });
console.log(`GDPR has ${page.meta.total} recitals`);
for (const recital of page.data) {
const preview = recital.content.slice(0, 80);
console.log(`Recital ${recital.recitalNumber}: ${preview}...`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | -------- | ----------------------------- |
| `frameworkSlug` | `string` | required | Framework slug, e.g. `"gdpr"` |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
***
### `get()`
Fetch a single recital by its number.
```typescript theme={null}
const recital = await client.recitals.get('gdpr', 1);
console.log(`Recital ${recital.recitalNumber}`);
console.log(recital.content);
```
**Parameters**
| Parameter | Type | Description |
| --------------- | -------- | -------------- |
| `frameworkSlug` | `string` | Framework slug |
| `recitalNumber` | `number` | Recital number |
**Returns** `Promise`
***
### `iter()`
Iterate over all recitals in a framework.
```typescript theme={null}
for await (const recital of client.recitals.iter('nis2')) {
console.log(`Recital ${recital.recitalNumber} (position ${recital.position})`);
}
```
**Parameters** — same options as `list()` (except `page`, which is managed internally).
**Returns** `AsyncGenerator`
***
## Model
### `Recital`
| Field | Type | Description |
| --------------- | -------- | ---------------------------------- |
| `id` | `number` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `recitalNumber` | `number` | Recital number |
| `content` | `string` | Full recital text |
| `position` | `number` | Display order within the framework |
***
## Example: extract recitals containing a keyword
```typescript theme={null}
const keyword = 'legitimate interest';
const matches: number[] = [];
for await (const recital of client.recitals.iter('gdpr')) {
if (recital.content.toLowerCase().includes(keyword)) {
matches.push(recital.recitalNumber);
}
}
console.log(`Recitals mentioning "${keyword}":`, matches.join(', '));
```
For more structured full-text search across recitals and articles, use `client.search.query()` with `resultType: 'recital'`.
# Requirements
Source: https://docs.law4devs.eu/sdks/typescript/requirements
List and iterate extracted compliance requirements with the Law4Devs TypeScript SDK.
## Overview
Requirements are structured compliance obligations extracted from regulation articles. Each requirement is linked back to its source article and paragraph, categorised by type, and optionally includes a compliance deadline and stakeholder roles.
***
## Methods
### `list()`
Returns a page of requirements, optionally filtered to a single framework.
```typescript theme={null}
// All requirements across all frameworks
const allPage = await client.requirements.list();
// Requirements for a specific framework
const gdprPage = await client.requirements.list({ frameworkSlug: 'gdpr' });
console.log(`GDPR has ${gdprPage.meta.total} requirements`);
for (const req of gdprPage.data) {
console.log(`Art. ${req.articleNumber} [${req.requirementType}]: ${req.requirementText.slice(0, 80)}...`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| --------------- | -------- | ------- | ------------------------------ |
| `frameworkSlug` | `string` | — | Filter to a specific framework |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
***
### `iter()`
Iterate over requirements, optionally filtered to a framework.
```typescript theme={null}
for await (const req of client.requirements.iter({ frameworkSlug: 'cra' })) {
console.log(`[${req.requirementType}] Art. ${req.articleNumber}: ${req.requirementText.slice(0, 60)}...`);
}
```
**Parameters** — same options as `list()` (except `page`, which is managed internally).
**Returns** `AsyncGenerator`
***
## Model
### `Requirement`
| Field | Type | Description |
| ---------------------- | ---------------- | --------------------------------------------------------------------- |
| `id` | `number` | Internal numeric ID |
| `frameworkSlug` | `string` | Parent framework slug |
| `articleNumber` | `number` | Source article number |
| `paragraphRef` | `string \| null` | Source paragraph reference, e.g. `"1"`, `"2a"` |
| `paragraphContent` | `string \| null` | Full text of the source paragraph |
| `requirementText` | `string` | Extracted requirement obligation text |
| `requirementType` | `string` | Category, e.g. `"obligation"`, `"prohibition"`, `"right"` |
| `complianceDeadline` | `string \| null` | ISO 8601 deadline date, if applicable |
| `linkedArticleNumbers` | `number[]` | Related article numbers |
| `stakeholderRoles` | `string[]` | Roles this requirement applies to, e.g. `["controller", "processor"]` |
| `tags` | `Tag[]` | Tags applied to the requirement |
| `createdAt` | `string` | ISO 8601 creation timestamp |
***
## Example: build a compliance checklist
```typescript theme={null}
interface ChecklistItem {
id: number;
article: string;
type: string;
text: string;
deadline: string | null;
roles: string[];
}
async function buildChecklist(frameworkSlug: string): Promise {
const checklist: ChecklistItem[] = [];
for await (const req of client.requirements.iter({ frameworkSlug })) {
checklist.push({
id: req.id,
article: `Art. ${req.articleNumber}${req.paragraphRef ? `(${req.paragraphRef})` : ''}`,
type: req.requirementType,
text: req.requirementText,
deadline: req.complianceDeadline,
roles: req.stakeholderRoles,
});
}
// Sort obligations before other types
checklist.sort((a, b) => {
if (a.type === 'obligation' && b.type !== 'obligation') return -1;
if (b.type === 'obligation' && a.type !== 'obligation') return 1;
return 0;
});
return checklist;
}
const craChecklist = await buildChecklist('cra');
console.log(`CRA compliance checklist: ${craChecklist.length} items`);
```
Requirements are extracted from article paragraphs through automated analysis. Cross-check against the source article text — accessible via `client.articles.get()` — for authoritative wording.
# Search
Source: https://docs.law4devs.eu/sdks/typescript/search
Full-text search across all frameworks with the Law4Devs TypeScript SDK.
## Overview
The search resource provides full-text search across all framework content — articles, recitals, and requirements. Results include a `matchContext` snippet showing the matched text in context.
***
## Methods
### `query()`
Execute a full-text search and return a page of results.
```typescript theme={null}
const results = await client.search.query('data breach notification', {
framework: 'gdpr',
resultType: 'article',
page: 1,
perPage: 20,
});
console.log(`Found ${results.meta.total} matching articles`);
for (const result of results.data) {
console.log(`[${result.type}] ${result.frameworkName} Art. ${result.articleNumber}: ${result.title}`);
console.log(` ...${result.matchContext}...`);
console.log(` ${result.url}`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| ------------ | -------- | -------- | -------------------------------------------------------------------- |
| `query` | `string` | required | Search terms |
| `framework` | `string` | — | Filter results to a specific framework slug |
| `resultType` | `string` | — | Filter by content type: `"article"`, `"recital"`, or `"requirement"` |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
`meta.pages` is always `0` for search results. This is an endpoint limitation — use `meta.total` and `links.next` to determine if more results exist.
***
### `iter()`
Iterate over all search results without managing pagination.
```typescript theme={null}
for await (const result of client.search.iter('security requirements')) {
console.log(`[${result.type}] ${result.frameworkName} — ${result.title ?? result.requirementType}`);
}
```
**Parameters** — same options as `query()` (except `page`, which is managed internally).
**Returns** `AsyncGenerator`
***
## Model
### `SearchResult`
| Field | Type | Description |
| ----------------- | ---------------- | --------------------------------------------------------- |
| `type` | `string` | Result type: `"article"`, `"recital"`, or `"requirement"` |
| `frameworkSlug` | `string` | Parent framework slug |
| `frameworkName` | `string` | Parent framework display name |
| `articleNumber` | `number \| null` | Article number (for article and requirement results) |
| `recitalNumber` | `number \| null` | Recital number (for recital results) |
| `paragraphRef` | `string \| null` | Source paragraph reference |
| `title` | `string \| null` | Article title (for article results) |
| `requirementType` | `string \| null` | Requirement type (for requirement results) |
| `matchContext` | `string` | Snippet of text surrounding the match |
| `url` | `string` | Deep link to the matched item in the API |
***
## Examples
### Search for a concept across all frameworks
```typescript theme={null}
const results = await client.search.query('legitimate interest');
for (const result of results.data) {
const ref = result.articleNumber
? `Art. ${result.articleNumber}`
: `Recital ${result.recitalNumber}`;
console.log(`${result.frameworkName} ${ref}: ...${result.matchContext}...`);
}
```
### Collect all requirement results mentioning a term
```typescript theme={null}
const requirementMatches: SearchResult[] = [];
for await (const result of client.search.iter('incident response', {
resultType: 'requirement',
})) {
requirementMatches.push(result);
}
console.log(`Found ${requirementMatches.length} matching requirements`);
```
Combine `resultType: 'article'` with `iter()` for a simple way to find every article across all frameworks that mentions a specific term — no need to iterate frameworks first.
# Tags
Source: https://docs.law4devs.eu/sdks/typescript/tags
List, retrieve, and iterate taxonomy tags with the Law4Devs TypeScript SDK.
## Overview
Tags are the Law4Devs taxonomy — a curated set of labels applied to articles and requirements across all frameworks. Using tags you can find related content spanning multiple regulations (e.g. all articles tagged `"data-breach"` across GDPR, NIS2, and CRA).
***
## Methods
### `list()`
Returns a page of tags.
```typescript theme={null}
const page = await client.tags.list({ perPage: 50 });
console.log(`${page.meta.total} tags in the taxonomy`);
for (const tag of page.data) {
console.log(`${tag.slug}: ${tag.name}`);
}
```
**Parameters**
| Parameter | Type | Default | Description |
| --------- | -------- | ------- | ---------------------- |
| `page` | `number` | `1` | Page number |
| `perPage` | `number` | `20` | Items per page (1–100) |
**Returns** `Promise>`
***
### `get()`
Fetch a single tag by its slug.
```typescript theme={null}
const tag = await client.tags.get('data-breach');
console.log(tag.name);
console.log(tag.description);
console.log('Keywords:', tag.keywords.join(', '));
```
**Parameters**
| Parameter | Type | Description |
| --------- | -------- | -------------------------------------------- |
| `slug` | `string` | Tag slug, e.g. `"data-breach"`, `"security"` |
**Returns** `Promise`
***
### `iter()`
Iterate over all tags.
```typescript theme={null}
for await (const tag of client.tags.iter()) {
console.log(`#${tag.slug} — ${tag.name} (color: ${tag.color ?? 'none'})`);
}
```
**Parameters** — same options as `list()` (except `page`, which is managed internally).
**Returns** `AsyncGenerator`
***
## Model
### `Tag`
| Field | Type | Description |
| ------------- | ---------------- | ---------------------------------------------- |
| `id` | `number` | Internal numeric ID |
| `slug` | `string` | URL-safe identifier, e.g. `"data-breach"` |
| `name` | `string` | Display name, e.g. `"Data Breach"` |
| `description` | `string \| null` | Plain-text description of what this tag covers |
| `keywords` | `string[]` | Keywords used for auto-tagging |
| `color` | `string \| null` | Hex color for UI display, e.g. `"#e53e3e"` |
| `createdAt` | `string` | ISO 8601 creation timestamp |
***
## Example: build a tag index
```typescript theme={null}
const tagIndex = new Map();
for await (const tag of client.tags.iter()) {
tagIndex.set(tag.slug, {
name: tag.name,
keywords: tag.keywords,
});
}
console.log(`Indexed ${tagIndex.size} tags`);
console.log(tagIndex.get('security'));
```
# Authentication
Source: https://docs.law4devs.eu/security/authentication
All Law4Devs API endpoints require an API key.
## API Key Required
Every request to the Law4Devs API must include a valid API key. Pass it via the `X-API-Key` header:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://api.law4devs.eu/v1/frameworks
```
```python theme={null}
from law4devs import Law4DevsClient
client = Law4DevsClient(api_key="YOUR_API_KEY")
```
```typescript theme={null}
import { Law4DevsClient } from 'law4devs';
const client = new Law4DevsClient({ apiKey: 'YOUR_API_KEY' });
```
```php theme={null}
use Law4Devs\Client;
$client = new Client(apiKey: 'YOUR_API_KEY');
```
Never hardcode your API key in source code. Use environment variables.
## Environment Variables
```python theme={null}
import os
from law4devs import Law4DevsClient
client = Law4DevsClient(api_key=os.environ["LAW4DEVS_API_KEY"])
```
```typescript theme={null}
import { Law4DevsClient } from 'law4devs';
const client = new Law4DevsClient({
apiKey: process.env.LAW4DEVS_API_KEY!,
});
```
```php theme={null}
use Law4Devs\Client;
$client = new Client(apiKey: getenv('LAW4DEVS_API_KEY'));
```
Get your API key at [law4devs.eu](https://law4devs.eu).
# CORS Policy
Source: https://docs.law4devs.eu/security/cors-policy
Cross-origin request configuration for browser-based integrations
Law4Devs supports cross-origin requests so you can call the API directly from browser
applications. CORS is configured at the infrastructure level — no extra setup is needed
for most use cases.
## Allowed Configuration
| Property | Value |
| ------------------- | -------------------------------------------- |
| **Allowed methods** | `GET`, `OPTIONS` |
| **Allowed origins** | `*` |
| **Allowed headers** | `Content-Type`, `Authorization`, `X-API-Key` |
| **Preflight cache** | 3600 seconds (1 hour) |
The API is read-only — only `GET` requests are permitted. `OPTIONS` is allowed for
CORS preflight checks.
## Browser Example
```typescript theme={null}
const response = await fetch(
'https://api.law4devs.eu/v1/frameworks',
{
headers: {
'X-API-Key': import.meta.env.VITE_LAW4DEVS_API_KEY,
},
}
);
const data = await response.json();
console.log(data.data);
```
Never expose your API key in a browser application's source code or public repository.
Use environment variables and server-side proxying for production browser integrations.
# Rate Limits
Source: https://docs.law4devs.eu/security/rate-limits
Per-plan request rate limits for the Law4Devs API.
## Rate Limits by Plan
| Plan | Req/min | Req/day | Monthly cap | Max per\_page |
| ------ | ------- | ------- | ----------- | ------------- |
| Growth | 10 | 500 | 10,000 | 10 |
| Pro | 30 | 5,000 | 100,000 | 25 |
| Scale | 100 | 50,000 | Unlimited | 50 |
| Custom | Custom | Custom | Unlimited | 100 |
When you exceed the rate limit, the API returns **HTTP 429**:
```json theme={null}
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests."
}
}
```
All SDKs automatically retry on 429 with exponential backoff (up to 3 retries by default). You can control this with `max_retries` / `maxRetries`:
```python theme={null}
client = Law4DevsClient(
api_key=os.environ["LAW4DEVS_API_KEY"],
max_retries=5,
)
```
```typescript theme={null}
const client = new Law4DevsClient({
apiKey: process.env.LAW4DEVS_API_KEY!,
maxRetries: 5,
});
```
```php theme={null}
$client = new Client(
apiKey: getenv('LAW4DEVS_API_KEY'),
maxRetries: 5,
);
```
```rust theme={null}
let client = Law4DevsClient::builder()
.api_key(std::env::var("LAW4DEVS_API_KEY").unwrap())
.max_retries(5)
.build();
```
## Plan Comparison
| Plan | Monthly | Annual | Req/min | Req/day | Monthly cap | Max per\_page | API Keys | Support | Uptime SLA |
| ---------- | ---------- | ------ | ------- | ------- | ----------- | ------------- | --------- | --------------------------------- | ---------- |
| **Growth** | €29 | €290 | 10 | 500 | 10,000 | 10 | 10 | Email (48h) | 99.5% |
| **Pro** | €89 | €890 | 30 | 5,000 | 100,000 | 25 | 50 | Priority email + Slack (24h) | 99.9% |
| **Scale** | €229 | €2,290 | 100 | 50,000 | Unlimited | 50 | 100 | Dedicated Slack + account manager | 99.9% |
| **Custom** | Contact us | — | Custom | Custom | Unlimited | 100 | Unlimited | Dedicated Slack + account manager | 99.99% |
Upgrade your plan at [law4devs.eu](https://law4devs.eu).
# Security Headers
Source: https://docs.law4devs.eu/security/security-headers
HTTP response headers Law4Devs sets on every API response
Every response from the Law4Devs API includes a set of security headers designed to protect
both the API and its consumers. These headers are set automatically — no configuration
required on your end.
## Response Headers
| Header | Value | Purpose |
| ------------------------ | --------------------------------- | ------------------------------------------------------------------- |
| `X-Content-Type-Options` | `nosniff` | Prevents MIME-type sniffing attacks in browsers |
| `X-Frame-Options` | `DENY` | Prevents the API response from being embedded in iframes |
| `X-XSS-Protection` | `1; mode=block` | Legacy XSS filter for older browsers |
| `Referrer-Policy` | `strict-origin-when-cross-origin` | Controls how much referrer information is included |
| `X-API-Version` | `1.0` | Signals the current API version on every response |
| `X-Request-ID` | UUID (per-request) | Unique identifier for each request — use this when reporting issues |
The `Server` header is stripped from all responses to avoid fingerprinting the underlying
infrastructure.
## Using `X-Request-ID`
Every response carries a unique `X-Request-ID` header. Log this value alongside your
application errors — it lets the Law4Devs team trace a specific request end-to-end when
you report an issue.
```bash theme={null}
curl -v -H "X-API-Key: YOUR_API_KEY" \
https://api.law4devs.eu/v1/frameworks 2>&1 | grep X-Request-ID
# < X-Request-ID: 4f2a1c8e-9b3d-47e2-a1f0-8c5d6e7b9a2c
```
## Using `X-API-Version`
The `X-API-Version` header is included in every response so your application can detect
version changes without parsing the URL.
```python theme={null}
import requests
response = requests.get(
"https://api.law4devs.eu/v1/frameworks",
headers={"X-API-Key": "YOUR_API_KEY"},
)
api_version = response.headers.get("X-API-Version")
print(f"API version: {api_version}") # API version: 1.0
```