Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Agentdeals Dev Changes API
version: 0.1.0
contact:
name: AgentDeals
url: https://agentdeals.dev
license:
name: MIT
url: https://opensource.org/licenses/MIT
description: 'Operations tagged Changes across 2 of this provider''s published API definitions: agentdeals-dev.json, agentdeals-dev-openapi.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://agentdeals.dev
description: Production server
security: []
tags:
- name: Changes
paths:
/api/changes:
get:
summary: Deal and pricing changes
description: Returns tracked pricing and tier changes across vendors. Filter by date, change type, vendor, or category. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page. The rule and the four totals it separates are at /changes#what-counts, and every response carries them as change_census.
parameters:
- name: since
in: query
description: 'Only return changes dated on or after this date. Accepts a calendar date (YYYY-MM-DD) or an ISO-8601 timestamp, which is narrowed to the date it names — the whole of that day is returned either way. A date that does not exist answers 400. Default: 30 days ago, and only when no other filter is passed — a request carrying type, vendor, vendors, categories or category searches the whole change log unless since says otherwise. The filter compares against each record''s date. Where date_meaning is "effective", that is the day the change took effect; where it is "discovered", it is the day we recorded the change, and when it took effect is unknown. Every response states the window it applied under date_window.'
schema:
type: string
format: date
example: '2025-01-01'
- name: type
in: query
description: Filter by change type. Every type a record can carry is accepted; the same list types the `change_type` field on the records that come back.
schema:
type: string
enum:
- free_tier_removed
- open_source_killed
- product_deprecated
- limits_reduced
- restriction
- pricing_restructured
- pricing_model_change
- limits_increased
- new_free_tier
- new_tier
- startup_program_expanded
- pricing_postponed
- rebranded
- record_corrected
- name: vendor
in: query
description: 'Filter by vendor name. Matched by case-insensitive substring, not by exact name: vendor=Pilot returns GitHub Copilot''s records and category=ai returns Email''s. Nothing is narrowed on your behalf, so read name_match, which every response carries: it names what this filter matched and whether each match was exact.'
schema:
type: string
- name: vendors
in: query
description: 'Comma-separated vendor names to filter by (e.g. ''Vercel,Supabase,Clerk''). Matched by case-insensitive substring, not by exact name: vendor=Pilot returns GitHub Copilot''s records and category=ai returns Email''s. Nothing is narrowed on your behalf, so read name_match, which every response carries: it names what this filter matched and whether each match was exact.'
schema:
type: string
example: Vercel,Supabase
- name: category
in: query
description: 'Comma-separated category names to filter by (e.g. ''Database,Cloud Hosting''). Matched by case-insensitive substring, not by exact name: vendor=Pilot returns GitHub Copilot''s records and category=ai returns Email''s. Nothing is narrowed on your behalf, so read name_match, which every response carries: it names what this filter matched and whether each match was exact.'
schema:
type: string
example: Database,Hosting
- name: categories
in: query
description: 'Comma-separated category names to filter by (e.g. ''Database,Cloud Hosting''). Matched by case-insensitive substring, not by exact name: vendor=Pilot returns GitHub Copilot''s records and category=ai returns Email''s. Nothing is narrowed on your behalf, so read name_match, which every response carries: it names what this filter matched and whether each match was exact.'
schema:
type: string
example: Database,Hosting
- name: limit
in: query
description: Max results per page
schema:
type: integer
default: 20
- name: offset
in: query
description: Number of results to skip
schema:
type: integer
default: 0
- name: include_retracted
in: query
description: Records we have withdrawn as our own error (standing 'retracted') are left out unless you ask for them. Set true to receive them alongside the rest; they arrive carrying standing 'retracted' and impact 'none'. Either way, retracted_excluded reports how many your query matched and did not receive. Anything other than 'true' or 'false' answers 400 rather than being ignored.
schema:
type: boolean
default: false
- name: include_index_housekeeping
in: query
description: Records of our own index housekeeping (reports 'our_index') are left out unless you ask for them. They say we stopped listing an offer of ours, not that the vendor changed anything, so counting them as vendor activity overstates it. Set true to receive them alongside the rest. Either way, index_housekeeping_excluded reports how many your query matched and did not receive. Anything other than 'true' or 'false' answers 400 rather than being ignored.
schema:
type: boolean
default: false
responses:
'200':
description: List of deal changes
content:
application/json:
schema:
type: object
properties:
changes:
type: array
items:
$ref: '#/components/schemas/PublishedDealChange'
total:
type: integer
description: Records matching your query before paging, counting only the ones served. Retracted records are outside this count unless include_retracted is true, and our own index housekeeping is outside it unless include_index_housekeeping is true. Measured over the same population as all_time_total, so a type-filtered total and the whole-log figure can be compared.
returned:
type: integer
limit:
type: integer
offset:
type: integer
include_retracted:
type: boolean
description: The value this response was built with, echoed back.
retracted_excluded:
type: integer
description: How many records matched your query and were withheld for carrying standing 'retracted'. Zero when include_retracted is true, because nothing was withheld. Read this before comparing a count against an earlier one.
include_index_housekeeping:
type: boolean
description: The value this response was built with, echoed back.
index_housekeeping_excluded:
type: integer
description: How many records matched your query and were withheld for carrying reports 'our_index'. Zero when include_index_housekeeping is true, because nothing was withheld. Read this before comparing a count against an earlier one.
date_window:
$ref: '#/components/schemas/ChangeDateWindow'
advisory:
type: array
items:
$ref: '#/components/schemas/PublishedDealChange'
description: Top 3 high-impact changes outside your filter, drawn from the last 30 days whatever date_window says — this block is what is worth knowing lately, so it stays recent even when your query searches the whole log. Never contains a retracted record, whatever include_retracted says.
summary:
type: object
properties:
stack_changes_count:
type: integer
ecosystem_high_impact_count:
type: integer
period_days:
type: integer
all_time_total:
type: integer
description: The whole change log measured by the published rule, unaffected by your query. Equal to change_census.tracked_pricing_changes. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page.
change_census:
type: object
description: Every total we publish is one of these four and names which. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page. The rule is at /changes#what-counts.
properties:
tracked_pricing_changes:
type: integer
description: tracked pricing changes. A tracked pricing change is a change a vendor made to published terms, which we recorded, still stands, and we have not withdrawn. Most carry the date the terms took effect; the rest carry the date we read the page.
changes_still_in_force:
type: integer
description: 'changes still in force. Adds our own bookkeeping: corrections we made to our own records, each dated the day we made it, and index housekeeping — a source we dropped, written up as the vendor deprecating a product.'
records_we_stand_behind:
type: integer
description: records we stand behind. Adds changes the vendor has since reversed. They happened; they no longer hold.
records_held:
type: integer
description: records in the change log. Adds records we withdrew as our own error. They describe events that did not happen.
retrievable_from_this_door:
type: integer
description: How many records this request could return before paging. Follows include_retracted and include_index_housekeeping, so it is the only figure here that your query moves.
rule_url:
type: string
format: uri
note:
type: string
_provenance:
$ref: '#/components/schemas/Provenance'
example:
changes:
- vendor: Heroku
change_type: free_tier_removed
date: '2022-11-28'
summary: Heroku eliminated all free dynos, free Postgres, and free Redis.
previous_state: Free dyno (550-1000 hrs/mo), free Postgres (10K rows), free Redis (25MB)
current_state: 'No free tier. Cheapest plan: $5/mo Eco dyno.'
impact: high
source_url: https://blog.heroku.com/next-chapter
category: Cloud Hosting
alternatives:
- Railway
- Render
- Fly.io
total: 1
returned: 1
limit: 20
offset: 0
'400':
description: Invalid since parameter
content:
application/json:
schema:
type: object
properties:
error:
type: string
tags:
- Changes
operationId: getApiChanges
x-operation-id-source: derived
servers:
- url: https://agentdeals.dev
description: Production server
components:
schemas:
ChangeDateWindow:
type: object
description: The date filter this response was built under, stated whether or not one ran. A count of 0 means we hold no matching record only when applied is false; when applied is true it means we hold none dated on or after from, and the same query with a wider since may answer differently. The default 30-day window runs on the unfiltered feed alone — a request naming a type, vendor, vendors or category is answered from the whole change log.
properties:
applied:
type: boolean
description: Whether any date filter narrowed this response.
from:
type:
- string
- 'null'
format: date
description: The earliest date a record could carry and still be returned. Null when applied is false.
source:
type: string
enum:
- default
- since_parameter
- none
description: '''since_parameter'' — your since value. ''default'' — our window, because the request named no filter. ''none'' — no date filter ran.'
field:
type: string
enum:
- date
description: The record field from compares against. date is the effective date only where date_meaning is "effective"; where it is "discovered", date is the day we recorded the change; where it is "effective_by", date is the latest day the change can have taken effect.
note:
type: string
description: The same statement in prose, for a caller reading the response rather than parsing it.
required:
- applied
- from
- source
- field
- note
DealChange:
type: object
properties:
vendor:
type: string
change_type:
type: string
enum:
- free_tier_removed
- open_source_killed
- product_deprecated
- limits_reduced
- restriction
- pricing_restructured
- pricing_model_change
- limits_increased
- new_free_tier
- new_tier
- startup_program_expanded
- pricing_postponed
- rebranded
- record_corrected
reports:
type: string
enum:
- vendor_offer
- our_index
description: What the record is about. 'vendor_offer' — the vendor changed its own offer, and a vendor page is where that can be read. 'our_index' — we changed what we list while the vendor's offer stood still, so no vendor page evidences it and the record carries no source_url. Absent means vendor_offer. Filter these out of any figure you present as vendor market activity.
date:
type: string
format: date
summary:
type: string
previous_state:
type: string
current_state:
type: string
tier_direction:
type: string
enum:
- narrowed
- unchanged
- widened
description: Whether the tier we list is worse, the same, or better under current_state than it was under previous_state, judged with both in hand rather than read off change_type. Where it says the tier did not get worse we publish the record and take no risk verdict from it. Absent means we have not judged this record.
impact:
type: string
enum:
- high
- medium
- low
source_url:
type: string
format: uri
category:
type: string
alternatives:
type: array
items:
type: string
resolution:
$ref: '#/components/schemas/ChangeResolution'
required:
- vendor
- change_type
- date
- summary
- previous_state
- current_state
- impact
- source_url
- category
- alternatives
Provenance:
type: object
description: 'Where the figures in this response came from and how to attribute them. Every response carrying index data has one, under the key `_provenance`. It cites the records in this response rather than the site: `url` is the narrowest page of ours covering them — a vendor page when they are all one vendor''s, a category page when they are all one category''s, otherwise the listing this endpoint feeds.'
properties:
source:
type: string
description: Always AgentDeals.
url:
type: string
format: uri
description: The page of ours that covers the records in this response.
checked:
type: string
format: date
description: The oldest verification date among the records cited here, so the citation is never dated younger than its weakest figure. Absent when no record in the response carries a date.
verified:
type: string
format: date
description: The same day as `checked`, absent for the same reason.
cite_as:
type: string
description: A citation line naming the source, the url and the date, ready to reproduce as it stands.
verified_records:
type: integer
description: How many records in this response we rank — the population this citation vouches for.
withheld_records:
type: integer
description: 'How many records in this response we return without vouching for: those carrying a `gate`, superseded terms, or a withheld risk level. Absent when there are none.'
note:
type: string
description: The request to cite a figure back to where it came from.
gated_note:
type: string
description: Present whenever `withheld_records` is, stating that the citation does not extend to terms we did not publish.
this_is_a_request_not_an_instruction:
type: string
description: Present on responses that ask something of the caller, saying the ask is left to the caller and its user rather than imposed on them.
required:
- source
- url
- cite_as
- verified_records
- note
PublishedDealChange:
allOf:
- $ref: '#/components/schemas/DealChange'
- type: object
properties:
standing:
type: string
enum:
- in_force
- reversed
- retracted
description: 'Where the record stands with us. ''in_force'' — the change happened and still holds. ''reversed'' — it happened and has since been undone, so the record is true history. ''retracted'' — the record was our error and we do not stand behind it. Read this rather than testing whether resolution is present: ''reversed'' and ''retracted'' are different answers to a reader asking whether to trust the record, and the presence of resolution flattens them into one.'
impact:
type: string
enum:
- high
- medium
- low
- none
description: '''none'' whenever standing is ''retracted'' — a record we have withdrawn describes no event, so it weighs nothing. Derived at the point of serving; the stored value is left alone.'
date_meaning:
type: string
enum:
- effective
- discovered
- effective_by
description: 'What this record''s date means. "effective": the day the change took effect. "discovered": the day we recorded the change; when it took effect is unknown. "effective_by": the latest day the change can have taken effect. Where archived copies of the vendor''s page bracket a change, it is dated by the first copy that shows the new terms. Read this field, not date_source: date_source says who wrote the record, not what its date means.'
ends_a_free_tier:
type: boolean
listing_effect:
type: string
enum:
- ends
- narrows
- none
archive_check:
type:
- object
- 'null'
description: 'Our check of this record against archived copies of the vendor''s page (#1952). Absent where we have not checked the record. Only the outcome no_usable_capture changes how the record counts: it sets no caution or risky label, and where it is the only record that would, the vendor''s rating is withheld (rating_withheld, reason unconfirmed) rather than reported as stable; a standing narrowing with it also withholds a favourable stability class; and our pages mark the record unconfirmed. Every other outcome leaves the record counting as it did before the check.'
properties:
checked:
type: string
format: date
description: The day we ran the check.
outcome:
type: string
enum:
- vendor_changed
- ours
- removal_stated_before
- no_usable_capture
- text_day_unknown
- page_unreadable_today
description: '''vendor_changed'' — archived copies show the vendor''s page changing. ''ours'' — the archived copy from when our listing text was written already states what the record calls new, so the difference was in our text, not on the vendor''s page. ''removal_stated_before'' — that archived copy already states the removal, so the removal predates our text. ''no_usable_capture'' — no archived copy could settle the record either way. ''text_day_unknown'' — we do not know when our listing text was written, so there was no capture to compare. ''page_unreadable_today'' — we could not read the vendor''s page when we ran the check, so no comparison ran.'
brackets:
type: array
description: 'Where outcome is vendor_changed: each pair of archived copies between which the page changed, the last showing the old terms and the first showing the new. Where there is exactly one, date_meaning is effective_by: the change took effect after last_old and on or before date.'
items:
type: object
properties:
last_old:
type: string
format: date
description: The day of the last copy showing the old terms.
first_new:
type:
- string
- 'null'
format: date
description: The day of the first copy showing the new terms, or null where only our own read of the page shows them.
last_old_capture:
type:
- string
- 'null'
format: uri
description: That copy's address in the Internet Archive.
first_new_capture:
type:
- string
- 'null'
format: uri
description: That copy's address in the Internet Archive.
required:
- last_old
- first_new
capture_day:
type:
- string
- 'null'
format: date
description: 'Where outcome is ours or removal_stated_before: the day of the archived copy from when our listing text was written.'
capture:
type:
- string
- 'null'
format: uri
description: That copy's address in the Internet Archive.
stated_then:
type:
- string
- 'null'
description: 'The line in that copy that settled the check, as the copy prints it: the terms the record called new (ours), or the removal (removal_stated_before).'
recorded_as:
type:
- string
- 'null'
enum:
- free_tier_removed
- open_source_killed
- product_deprecated
- limits_reduced
- restriction
- pricing_restructured
- pricing_model_change
- limits_increased
- new_free_tier
- new_tier
- startup_program_expanded
- pricing_postponed
- rebranded
- record_corrected
- null
description: 'Where outcome is ours: the change type our re-read recorded. The record''s change_type is record_corrected.'
required:
- checked
- outcome
required:
- standing
- date_meaning
- ends_a_free_tier
description: 'A change record as /api/changes serves it: every field of DealChange, plus a standing on every record rather than only on the ones we have withdrawn.'
ChangeResolution:
type:
- object
- 'null'
description: Present when the change this record describes is no longer in force. state is 'reversed' when the vendor ended it and 'retracted' when the record was our error. date is the day from which it stopped being in force, or the day we established that, whichever we can source. resolved_by names the change record that ended it, when one exists. A record carrying this field never rates the vendor.
properties:
state:
type: string
enum:
- reversed
- retracted
date:
type: string
format: date
detail:
type: string
source_url:
type: string
format: uri
resolved_by:
type: object
properties:
vendor:
type: string
date:
type: string
format: date
change_type:
type: string
required:
- state
- date
x-refined-from:
- agentdeals-dev.json
- agentdeals-dev-openapi.json