Project overview
The Open Podcast API project is inspired by and builds upon the gPodder API. In the summer of 2019, the creator of gPodder announced they were stepping down and the community took over.1 Unfortunately, about a year later the project entered 'basic maintenance mode' due to shifting priorities of key contributors. The limited availability of volunteers combined with gPodder.net's popularity among end-users meant that people started to see server errors while synchronizing or creating an account.2 3 Attempts to establish contact and collaborate on improving the situation didn't work out as hoped. Given the situation, AntennaPod contributors started discussing whether gPodder.net support should be removed4 or whether it could be forked. They concluded that the best solution would be to create a new API spec with a broad range of contributors to allow users to switch servers (avoiding major loads on a single server or project), to provide an opportunity to more easily go beyond the existing gPodder.net API specs, and to enable developers to address some technical issues with the API specs.
The initial discussions on GitHub led to a meeting in October 2022 with contributors from AntennaPod, Funkwhale, Kasts, Podfriend and the gPodder app for Nextcloud.5 A few months later, the first of the recurring meetings took place to start developing the specification.
Join us!
We encourage you to engage in the discussions, and provide feedback based on your implementation. See the contribution page how you can get involved.
Code of Conduct
The Open Podcast API project abides by the Contributor Covenant Code of Conduct. Please familiarize yourself with it before participating in any of our community spaces.
Licensing
Our project builds on and is itself open source:
- Documentation: Creative Commons Attribution-ShareAlike 4.0 International Public License
- Reference implementations: MIT
-
Questions about gPodder.net issues on the AntennaPod forum ↩
-
Suggestion on the AntennaPod forum to remove gPodder.net support ↩
-
Initial discussions on the needs for a new podcast synchronisation API ↩
Contribute
The Open Podcast API initiative builds on open source and open standards. Contributors from different projects work together to develop the specifications in the open.
We invite all projects and individuals to join us in developing these specifications. This ensures that we take all perspectives into account and create a set of interoperable specs.
Contributor Covenant Code of Conduct
Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
Our Standards
Examples of behavior that contributes to a positive environment for our community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement via conduct AT openpodcastapi DOT org. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
1. Correction
Community Impact: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
Consequence: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
2. Warning
Community Impact: A violation through a single incident or series of actions.
Consequence: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
3. Temporary Ban
Community Impact: A serious violation of community standards, including sustained inappropriate behavior.
Consequence: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
4. Permanent Ban
Community Impact: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
Consequence: A permanent ban from any sort of public interaction within the community.
Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.
For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
Conventions
All specifications are currently 'in progress'. Breaking changes can occur as we implement specifications and address issues.
Follow these conventions when contributing to the specs.
Keywords
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 RFC2119 RFC8174 when, and only when, they appear in all capitals, as shown here.
Core and optional functionality
To ensure that the end-user experience is consistent across implementations, the specifications mark endpoints and features as Core (required) and Optional.
- Core: The feature or endpoint MUST be supported by all clients and servers.
- Optional: The feature or endpoint is considered to be additional functionality. Clients and servers MAY optionally support any combination of these features. Any project implementing Optional functionality SHOULD inform users about what is supported.
Headers
The Open Podcast API follows JSON:API media type requirements. The following headers apply to all requests and responses:
Request headers
| Header | Required | Value | Description |
|---|---|---|---|
Content-Type | Yes | application/vnd.api+json | MUST be used when sending data to the server. |
Accept | Yes | application/vnd.api+json | MUST be used to indicate the client expects JSON:API-compliant responses. |
Response headers
| Header | Required | Value | Description |
|---|---|---|---|
Content-Type | Yes | application/vnd.api+json | MUST be present in all JSON:API responses. |
Location | Yes (201) | URI of created resource | MUST be included in 201 Created responses to identify the new resource. |
Pagination
Clients MAY paginate resource collections using the page query parameter object.
Request format
The server MUST support pagination using the following query parameters:
| Parameter | Type | Description |
|---|---|---|
page[number] | int | The page of results to return. Defaults to 1. |
page[size] | int | The number of results to return per page. Defaults to 25, max: 100. |
Example:
GET /v1/subscriptions?page[number]=2&page[size]=50
Response format
The server MUST include the following in paginated responses:
links
The top-level links object MUST include pagination links using the following keys:
| Link key | Description |
|---|---|
self | The current page's full request URI. |
first | The URI for the first page of results. |
prev | The URI for the previous page, or null if the current page is first. |
next | The URI for the next page, or null if the current page is last. |
last | The URI for the last page of results. |
Example:
"links": {
"self": "/v1/subscriptions?page[number]=2&page[size]=25",
"first": "/v1/subscriptions?page[number]=1&page[size]=25",
"prev": "/v1/subscriptions?page[number]=1&page[size]=25",
"next": "/v1/subscriptions?page[number]=3&page[size]=25",
"last": "/v1/subscriptions?page[number]=4&page[size]=25"
}
meta
The top-level meta object MAY include pagination metadata:
| Field | Type | Description |
|---|---|---|
total | int | Total number of items available (optional). |
Example:
"meta": {
"total": 100
}
Server behavior
- The server SHOULD enforce a maximum page size (e.g., 100 items).
- The server MUST return an empty
dataarray if the page is valid but contains no items. - The server MAY omit the
totalfield inmetaif computing it is expensive.
Compatibility
This pagination model is compliant with the JSON:API pagination specification.
Error codes
This page lists the common HTTP status codes used across the API. All error responses MUST conform to the JSON:API error object format.
Success status codes
200 OK
The request was successfully processed.
This status is typically used for GET, PATCH, or bulk POST operations when resources are modified or retrieved but not newly created.
201 Created
The request created a new resource.
The response will include a Location header with the URI of the created resource and a data object describing it.
Client error status codes
400 Bad Request
The request was malformed, invalid JSON, or structurally incorrect according to the JSON:API format.
This may include missing required members such as type, id, or attributes.
401 Unauthorized
Authentication is required and has failed or was not provided. The client MUST provide valid credentials to access the endpoint.
403 Forbidden
The client is authenticated but does not have permission to perform the requested action.
404 Not Found
The requested resource does not exist, or the URL is invalid.
405 Method Not Allowed
The HTTP method used is not supported for the target endpoint.
409 Conflict
The request could not be completed due to a conflict with the current state of the resource. For example, attempting to create a resource that already exists and cannot be duplicated.
415 Unsupported Media Type
The Content-Type of the request must be application/vnd.api+json.
This error is returned if the client uses an incorrect or missing media type.
422 Unprocessable Entity
The request is syntactically valid, but semantically invalid or fails domain-specific validation rules.
Server error status codes
500 Internal Server Error
A generic error indicating an unexpected server-side condition. Clients SHOULD NOT rely on this for predictable error handling.
Error object format
All error responses MUST return an array of error objects using the format defined by JSON:API.
{
"errors": [
{
"status": "422",
"title": "invalid field value",
"detail": "the provided guid is not a valid UUIDv5",
"source": { "pointer": "/data/id" },
"meta": {
"provided_guid": "1234-invalid-guid"
}
}
]
}
Required members
status: the HTTP status codetitle: a short summary of the errordetail: a longer human-readable explanation of the issue
Optional members
source.pointer: a JSON Pointer to the part of the request that caused the errormeta: additional non-standard metadata useful for debugging or client-side handling
Subscriptions
Overview
A subscription represents a podcast feed a user has subscribed to. Subscriptions are essential entities that link users to feeds, episodes, and other related data.
The subscriptions endpoint is used to synchronize subscriptions between a server and connected clients.
guid calculation
Subscriptions are identified by a globally unique guid value.
This guid is used as the id field of each subscription resource and acts as the canonical resource identifier.
Feed guid values MUST be created in accordance with the Podcast Index's methodology.
If a feed already has a valid UUIDv5 guid tag, the client MUST pass this value to the server when submitting the feed.
If the feed doesn't have a valid guid tag, the client MUST:
- Generate a UUIDv5
guidvalue using:- The feed URL (with protocol and trailing slashes removed).
- The Podcast namespace UUID:
ead4c236-bf58-58c6-a2c6-a6b28d128cb6.
- Pass the generated value as the
guidin the API request.
This process ensures that any feed not currently registered with the Podcast Index is identified by the exact same GUID it would receive if it were updated to the Podcasting 2.0 specification.
The server MUST reject invalid guid values with a 400 Bad Request response.
The server MUST NOT attempt to validate or recalculate the guid against the feed_url, as the origin URL may have changed since the guid was set.
Supported actions
Add a subscription
Enables clients to add a new subscription to the system and register the authenticated user as a subscriber.
POST /v1/subscriptions
Request format
The client MUST provide a single resource object with:
type: MUST be"subscription"id: MUST be theguid(UUIDv5) for the feed. Seeguidcalculationattributes.feed_url: The podcast feed URL
POST /v1/subscriptions HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
{
"data": {
"type": "subscription",
"id": "ce510f4d-9046-5590-846e-58619ab8b353",
"attributes": {
"feed_url": "https://example.com/rss1"
}
}
}
Server behavior
- The server MUST create the subscription or confirm the existing record.
- The operation MUST be idempotent.
- The server MUST return a
201 Createdresponse with the created resource. - If the resource already exists, the server MAY return a
200 OK.
Success response
If the subscription is created or confirmed successfully:
- HTTP Status:
201 Created - Headers:
Content-Type: application/vnd.api+jsonLocation: <URI of created resource>
The created resource MUST have the following attributes:
feed_url: the feed URL of the subscription targetuser_subscribed_at: the timestamp at which the user's subscription was created, in ISO 8601 format
The response MUST contain the following links.
self: a link to the created resourceunsubscribe:href: a link to the created resourcemethod: MUST beDELETE
HTTP/1.1 201 Created
Location: /v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353
Content-Type: application/vnd.api+json
{
"jsonapi": { "version": "1.1" },
"data": {
"type": "subscription",
"id": "ce510f4d-9046-5590-846e-58619ab8b353",
"attributes": {
"feed_url": "https://example.com/rss1",
"user_subscribed_at": "2025-08-24T16:00:00Z"
},
"links": {
"self": "/v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353",
"unsubscribe": {
"href": "/v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353",
"method": "DELETE"
}
}
}
}
Bulk operations
Clients MAY use the Atomic Operations extension for batch creation or updates. See Add subscriptions in bulk for more information.
List subscriptions
Retrieves a paginated list of podcast subscriptions associated with the authenticated user.
GET /v1/subscriptions
Request format
This endpoint supports optional query parameters for pagination:
| Parameter | Description |
|---|---|
page[number] | The page of results to return (default: 1). |
page[size] | The number of results to return per page (default: 25, max: 100). |
GET /v1/subscriptions?page[number]=1&page[size]=2 HTTP/1.1
Accept: application/vnd.api+json
Server behavior
- The server MUST return only subscriptions that belong to the authenticated user.
- The server MUST return pagination metadata and pagination links.
- The server MUST return an empty array if the user has no subscriptions.
- The server SHOULD support
page[size]values between 1 and 100.
Success response
- HTTP Status:
200 OK - Content-Type:
application/vnd.api+json
Each subscription resource in the response MUST include:
type:"subscription"id: the globally uniqueguidattributes.feed_url: the feed URLattributes.user_subscribed_at: the timestamp of when the user subscribed in ISO 8601 formatlinks.self: link to the subscription resourcelinks.unsubscribe: aDELETE-able link to unsubscribe from the feed
The top-level links object MUST include pagination links:
self: the current request URIfirst: the first page of resultsprev: the previous page, if one existsnext: the next page, if one existslast: the last page of results
The top-level meta object MAY include total count metadata.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": { "version": "1.1" },
"data": [
{
"type": "subscription",
"id": "ce510f4d-9046-5590-846e-58619ab8b353",
"attributes": {
"feed_url": "https://example.com/rss1",
"user_subscribed_at": "2025-08-24T16:00:00Z"
},
"links": {
"self": "/v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353",
"unsubscribe": {
"href": "/v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353",
"method": "DELETE"
}
}
},
{
"type": "subscription",
"id": "b80719b3-1485-57c0-9e55-fda2b8f7472b",
"attributes": {
"feed_url": "https://example.com/rss2",
"user_subscribed_at": "2025-08-24T16:00:00Z"
},
"links": {
"self": "/v1/subscriptions/b80719b3-1485-57c0-9e55-fda2b8f7472b",
"unsubscribe": {
"href": "/v1/subscriptions/b80719b3-1485-57c0-9e55-fda2b8f7472b",
"method": "DELETE"
}
}
}
],
"links": {
"self": "/v1/subscriptions?page[number]=1&page[size]=2",
"first": "/v1/subscriptions?page[number]=1&page[size]=2",
"prev": null,
"next": "/v1/subscriptions?page[number]=2&page[size]=2",
"last": "/v1/subscriptions?page[number]=5&page[size]=2"
},
"meta": {
"total": 10
}
}
Get a subscription
Enables clients to fetch a single subscription resource by its globally unique guid.
GET /v1/subscriptions/{guid}
Request format
Clients MUST send a GET request to the canonical URI of a subscription resource, using the guid as the identifier.
Path parameters
| Parameter | Type | Description |
|---|---|---|
guid | string | The globally unique identifier (UUIDv5) of the subscription resource. |
Headers
Clients MUST include the following headers:
Accept: application/vnd.api+json
GET /v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353 HTTP/1.1
Accept: application/vnd.api+json
Server behavior
- The server MUST return a
200 OKresponse with the subscription resource if it exists and is associated with the authenticated user. - If the subscription does not exist, the server MUST return a
404 Not Founderror object. - The server MUST validate that the
guidis a valid UUID format. If it is not, the server MUST return a400 Bad Request.
Success response
If the subscription is found and accessible to the current user:
- HTTP Status:
200 OK - Headers:
Content-Type: application/vnd.api+json
The response body MUST include:
- A top-level
dataobject containing thesubscriptionresource. - A
links.selfpointing to the resource. - A
links.unsubscribepointing to the same resource withmethod: DELETE.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": { "version": "1.1" },
"data": {
"type": "subscription",
"id": "ce510f4d-9046-5590-846e-58619ab8b353",
"attributes": {
"feed_url": "https://example.com/rss1",
"user_subscribed_at": "2025-08-24T16:00:00Z"
},
"links": {
"self": "/v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353",
"unsubscribe": {
"href": "/v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353",
"method": "DELETE"
}
}
}
}
Error responses
| HTTP status | When it occurs |
|---|---|
400 Bad Request | If the provided guid is not a valid UUID. |
404 Not Found | If the subscription does not exist or is inaccessible. |
See error response format for more information.
Delete a subscription
Enables clients to remove a subscription for the authenticated user by guid.
DELETE /v1/subscriptions/{guid}
Request format
Clients MUST send a DELETE request to the canonical URI of a subscription resource.
Path parameters
| Parameter | Type | Description |
|---|---|---|
guid | string | The globally unique identifier (UUIDv5) of the subscription resource. |
Headers
Clients MUST include the following header:
Accept: application/vnd.api+json
DELETE /v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353 HTTP/1.1
Accept: application/vnd.api+json
Server behavior
- The server MUST remove the subscription for the authenticated user.
- The deletion MUST NOT affect other users subscribed to the same feed.
- The server MUST return a
204 No Contentif the subscription was deleted. - If no subscription exists for the user, the server MUST return
404 Not Found. - If the
guidis not a valid UUID, the server MUST return400 Bad Request.
Success response
If the subscription is successfully deleted:
- HTTP Status:
204 No Content - The response MUST NOT include a body.
HTTP/1.1 204 No Content
Error responses
| HTTP status | When it occurs |
|---|---|
400 Bad Request | If the provided guid is not a valid UUID. |
404 Not Found | If the subscription does not exist or is inaccessible. |
See error response format for more information.
Operations endpoint
The operations endpoint allows clients to perform multiple resource-level operations in a single, ordered request. It follows the JSON:API Atomic Operations extension and is intended for use cases where bulk writes or tightly ordered changes are needed.
POST /v1/operations
Request format
Clients MUST send a JSON document with an atomic:operations key containing an array of operation objects.
Each operation object MUST specify:
op: the operation type (e.g."add","update","remove")data: the resource object for the operation
Each operation MUST include a resource object with:
type: the resource type (e.g."subscription")id: the resource identifier, if applicableattributes: resource attributes as appropriate for the action
Headers
Clients MUST set the following headers:
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
POST /v1/operations HTTP/1.1
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Example request
{
"atomic:operations": [
{
"op": "add",
"data": {
"type": "subscription",
"id": "b80719b3-1485-57c0-9e55-fda2b8f7472b",
"attributes": {
"feed_url": "https://example.com/rss2"
}
}
},
{
"op": "add",
"data": {
"type": "subscription",
"id": "d29519a4-77ee-5e13-bb14-89fc93b993ae",
"attributes": {
"feed_url": "https://example.com/rss3"
}
}
}
]
}
Server behavior
- The server MUST process operations in order.
- Each operation MUST be executed independently.
- The server MUST return a
200 OKresponse even if one or more operations fail. - The response MUST include an
atomic:resultsarray with the same number of elements as the input. - Each
resultselement corresponds positionally to its respective request operation.
Success response format
For successful operations, the corresponding item in atomic:results MUST contain a data object.
Error response format
If an operation fails, the corresponding item MUST contain an errors array, conforming to the JSON:API error object format.
Response headers
The server MUST set the following headers:
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Supported operations
The operations endpoint may be used for:
Additional bulk actions (e.g. update, remove) may be supported in the future.
Clients MUST only use operations documented for this server.
Errors
For error semantics and structure, see error responses.
Errors may occur for:
- Validation failures (
422) - Resource conflicts (
409) - Type mismatches (
409or400) - Invalid resource identifiers (
400)
All errors are returned inline within the atomic:results array.
See also
Add subscriptions in bulk
POST /v1/operations
Creates multiple subscription resources.
See Subscriptions for more information.
Request
Clients MUST send an array of operations, each specifying:
op:"add"data: MUST contain a single resource object
Example
POST /api/v1/operations HTTP/1.1
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:operations": [
{
"op": "add",
"data": {
"type": "subscription",
"id": "ce510f4d-9046-5590-846e-58619ab8b353",
"attributes": {
"feed_url": "https://example.com/rss1"
}
}
},
{
"op": "add",
"data": {
"type": "subscription",
"id": "b80719b3-1485-57c0-9e55-fda2b8f7472b",
"attributes": {
"feed_url": "https://example.com/rss2"
}
}
}
]
}
Server behavior
- The server MUST attempt to perform each operation independently.
- Operations MUST be executed in order.
- The response MUST contain a
resultsarray with a one-to-one mapping of the input operations. - If any operation fails, the corresponding
resultsentry MUST be anerrorobject. - The server MUST return
200 OKregardless of partial failures.
Success response
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:results": [
{
"data": {
"type": "subscription",
"id": "ce510f4d-9046-5590-846e-58619ab8b353",
"attributes": {
"feed_url": "https://example.com/rss1",
"user_subscribed_at": "2025-08-24T16:00:00Z"
},
"links": {
"self": "/v1/subscriptions/ce510f4d-9046-5590-846e-58619ab8b353"
}
}
},
{
"data": {
"type": "subscription",
"id": "b80719b3-1485-57c0-9e55-fda2b8f7472b",
"attributes": {
"feed_url": "https://example.com/rss2",
"user_subscribed_at": "2025-08-24T16:00:00Z"
},
"links": {
"self": "/v1/subscriptions/b80719b3-1485-57c0-9e55-fda2b8f7472b"
}
}
}
]
}
Mixed success response
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic"
{
"atomic:results": [
{
"data": {
"type": "subscription",
"id": "ce510f4d-9046-5590-846e-58619ab8b353",
"attributes": {
"feed_url": "https://example.com/rss1",
"user_subscribed_at": "2025-08-24T16:00:00Z"
}
}
},
{
"errors": [
{
"status": "409",
"title": "Already Subscribed",
"detail": "User is already subscribed to this feed.",
"source": { "pointer": "/atomic:operations/1" },
"meta": {
"feed_url": "https://example.com/rss2",
"guid": "b80719b3-1485-57c0-9e55-fda2b8f7472b"
}
}
]
}
]
}