API documentation
- Best practices
- Listing package names
- List popular packages
- Searching for packages
- Getting package data
- Get package download stats
- Track package updates
- Get statistics
- List security advisories
- Create a package
- Edit a package
- Update a package
Best practices when using the Packagist.org API
- If you do scheduled jobs, avoid running things at midnight or once an hour at XX:00. Most people do so and we do see traffic peaks every hour. Pick a "random" time by hand for your cron jobs, or even better (if you can) is to make it run on a really randomized schedule.
- Send a User-Agent header with all your requests including an email or twitter or some sort of contact information so we can reach out to you if we have an issue with the way you use the API. If not you can leave us with no choice but to block IPs which we'd rather not do.
- If you send requests in parallel, be a good citizen and do a maximum of 10 concurrent requests, or up to 20 if you only fetch static files. Huge bursts in requests can cause issues for other regular users so try to spread out the load.
- Use an HTTP/2-capable client and make sure you enable that, it is much more efficient than reconnecting for every request.
Listing package names
All packages
GET https://packagist.org/packages/list.json
{
"packageNames": [
"[vendor]/[package]",
...
]
}
Working example: https://packagist.org/packages/list.json
List packages by organization
GET https://packagist.org/packages/list.json?vendor=[vendor]
{
"packageNames": [
"[vendor]/[package]",
...
]
}
Working example: https://packagist.org/packages/list.json?vendor=composer
List packages by type
GET https://packagist.org/packages/list.json?type=[type]
{
"packageNames": [
"[vendor]/[package]",
...
]
}
Working example: https://packagist.org/packages/list.json?type=composer-plugin
Retrieving additional data if the name is not enough
If you need more than just package names you can additionally request one or more fields to be returned with every package. Currently supported fields are: repository
, type
, and abandoned
.
Note that when requesting fields, the response structure is different.
GET https://packagist.org/packages/list.json?vendor=[type]&fields[]=type&fields[]=repository&fields[]=abandoned
{
"package": {
"[vendor]/[package]": {
"type": "library",
"repository": "https://...",
"abandoned": false|true|string (with advised replacement)
},
...
}
}
Working example: https://packagist.org/packages/list.json?vendor=composer&fields[]=repository&fields[]=type
List popular packages
If you need to retrieve the most popular (this is sorted by downloads over the last week, not overall downloads to ensure we demote formerly-popular packages) packages please use this endpoint to avoid having to request the download stats for all of our 300K+ packages individually. This also includes faver count (github stars + packagist.org favorites) and downloads count. The API is paginated if you need more than 100.
GET https://packagist.org/explore/popular.json?per_page=100
{
"packages": [
{
"name": "[vendor]/[package]",
"description": "[description]",
"url": "https://packagist.org/packages/[vendor]/[package]",
"downloads": 123456,
"favers": 123
},
...
],
"total": 123456,
"next": "https://packagist.org/explore/popular.json?page=2&per_page=100"
}
Working example: https://packagist.org/explore/popular.json?per_page=100
Searching for packages
Search results are paginated and you can change the pagination step by using the per_page parameter. For example https://packagist.org/search.json?q=[query]&per_page=5
Search packages by name
GET https://packagist.org/search.json?q=[query]
{
"results" : [
{
"name": "[vendor]/[package]",
"description": "[description]",
"url": "https://packagist.org/packages/[vendor]/[package]",
"repository": [repository url],
"downloads": [number of downloads],
"favers": [number of favers]
},
...
],
"total": [number of results],
"next": "https://packagist.org/search.json?q=[query]&page=[next page number]"
}
Working example: https://packagist.org/search.json?q=monolog
Search packages by tag
GET https://packagist.org/search.json?tags=[tag]
{
"results": [
{
"name": "[vendor]/[package]",
"description": "[description]",
"url": "https://packagist.org/packages/[vendor]/[package]",
"repository": "[repository url]",
"downloads": [number of downloads],
"favers": [number of favers]
}
...
],
"total": [numbers of results]
}
Working example: https://packagist.org/search.json?q=monolog&tags=psr-3
Search packages by type
GET https://packagist.org/search.json?q=[query]&type=symfony-bundle
{
"results" : [
{
"name": "[vendor]/[package]",
"description": "[description]",
"url": "https://packagist.org/packages/[vendor]/[package]",
"repository": [repository url],
"downloads": [number of downloads],
"favers": [number of favers]
},
...
],
"total": [number of results],
"next": "https://packagist.org/search.json?q=[query]&page=[next page number]"
}
Working example: https://packagist.org/search.json?q=monolog&type=symfony-bundle
Getting package data
Using the Composer v2 metadata
This is the preferred way to access the data as it is always up to date, and dumped to static files so it is very efficient on our end.
You can also send If-Modified-Since
headers to limit your bandwidth usage and cache the files on your end with the proper filemtime set according to our Last-Modified
header.
There are a few gotchas though with using this method:
- It only provides you with the package metadata but not information about the maintainers, download stats or github info.
- It is in a compressed format for efficiency which requires you to use
Composer\MetadataMinifier\MetadataMinifier::expand($response['packages'][$packageName])
from the composer/metadata-minifier package to restore it to the full data. - The
p2/$vendor/$package.json
file contains only tagged releases. If you want to fetch information about branches (i.e. dev versions) you need to downloadp2/$vendor/$package~dev.json
.
GET https://repo.packagist.org/p2/[vendor]/[package].json
{
"packages": {
"[vendor]/[package]": [
{
"name": "[vendor]/[package],
"description": [description],
"version": "[version1]",
// ...
},
{
"version": "[version2]",
// ...
}
// ...
]
},
"minified": "composer/2.0"
}
Working examples:
- For tagged releases:
https://repo.packagist.org/p2/monolog/monolog.json
- For dev releases:
https://repo.packagist.org/p2/monolog/monolog~dev.json
Looking to remain up to date and know when packages updated? See the Track package updates API.
Using the Composer v1 metadata (DEPRECATED)
You can also send If-Modified-Since
headers to limit your bandwidth usage and cache the files on your end with the proper filemtime set according to our Last-Modified
header.
There are a few gotchas though with using this method:
- It only provides you with the package metadata but not information about the maintainers, download stats or github info.
- It contains providers information which must be ignored but can appear confusing at first. This will disappear in the future though.
- It does not provide all packages due to the deprecation of the v1 metadata. See the announcement for details.
GET https://repo.packagist.org/p/[vendor]/[package].json
{
"packages": {
"[vendor]/[package]": {
"[version1]": {
"name": "[vendor]/[package],
"description": [description],
// ...
},
"[version2]": {
// ...
}
// ...
}
}
}
Working example: https://repo.packagist.org/p/monolog/monolog.json
Using the API
The JSON API for packages gives you all the infos we have including downloads, dependents count, github info, etc. However it is generated dynamically so for performance reason we cache the responses for twelve hours. As such if the static file endpoint described above is enough please use it instead.
GET https://packagist.org/packages/[vendor]/[package].json
{
"package": {
"name": "[vendor]/[package],
"description": [description],
"time": [packagist package creation datetime],
"maintainers": [list of maintainers],
"versions": [list of versions and their dependencies, the same data of composer.json]
"type": [package type],
"repository": [repository url],
"downloads": {
"total": [numbers of download],
"monthly": [numbers of download per month],
"daily": [numbers of download per day]
},
"favers": [number of favers]
}
}
Working example: https://packagist.org/packages/monolog/monolog.json
Get package download stats
If you need complete package information and use the JSON API already then please use the downloads
key to retrieve stats from that response.
However if you are only interested in download stats for a set of package names, you can use the stats endpoint which includes overall download stats + the faver count (github stars + packagist.org favorites):
GET https://packagist.org/packages/[vendor]/[package]/stats.json
{
"downloads": {
"total": 100,
"monthly": 10,
"daily": 1
},
"versions": [
"[list of versions]",
"[list of versions]"
],
"date": "2022-09-15" # stats collection start date
}
Working example: https://packagist.org/monolog/monolog/stats.json
Track package updates
This endpoint provides you with a feed of metadata changes you can poll to know what packages you need to update.
First to initialize this you can poll the API without timestamp to get the most current timestamp, or make your own by using 10000 * time()
:
GET https://packagist.org/metadata/changes.json
{
"error": "Invalid or missing "since" query parameter, make sure you store the timestamp at the initial point you started mirroring, then send that to begin receiving changes, e.g. https://packagist.org/metadata/changes.json?since=16142636710498 for example.",
"timestamp": 16142636710498
}
Working example: https://packagist.org/metadata/changes.json
After that, you should store the timestamp for the next time you want to call the API, let's say 10 minutes later you want to know what changed, you call this again but this time you pass the previous timestamp:
GET https://packagist.org/metadata/changes.json?since=16142636710498
{
"actions": [
{
"type": "update",
"package": "acme/package",
"time": 1614264954
},
{
"type": "update",
"package": "foo/bar~dev",
"time": 1614264951
},
{
"type": "delete",
"package": "acme/gone",
"time": 1614264953
}
]
}
Working example: https://packagist.org/metadata/changes.json?since=17343570960000
In the example above, you receive 3 changes, let's go over what they mean and what you should do to sync these up:
acme/update
was updated (tagged releases ofacme/update
), you can fetchhttps://repo.packagist.org/p2/acme/update.json
and should ensure that theLast-Modified
is AT LEAST (>=
) equal to thetime
value. If it is older than that, wait a few seconds and retry. Due to internal mirroring delays it may happen that you get a race condition and get an outdated file.foo/bar~dev
was updated (dev releases offoo/bar
, you can fetchhttps://repo.packagist.org/p2/foo/bar~dev.json
and should ensure that theLast-Modified
is AT LEAST (>=
) equal to thetime
value.acme/gone
was deleted, you can delete it on your end as well, this means bothacme/gone
andacme/gone~dev
are deleted.
Warning: The changes log is kept for up to 24h on our end, so make sure you fetch the API at least once a day or you will get a resync response like the following:
GET https://packagist.org/metadata/changes.json?since=16140636710498
{
"actions": [
{
"type": "resync",
"package": "*",
"time": 1614264954
}
]
}
If you get this, you should assume your data is stale and you should revalidate everything (if you cached files using Last-Modified
headers, you can still keep that and make sure with If-Modified-Since
requests for every file that it is still up to date).
Get statistics
This endpoint provides basic some statistics.
GET https://packagist.org/statistics.json
{
"totals": {
"downloads": [numbers of download]
}
}
Working example: https://packagist.org/statistics.json
List security advisories
This endpoint provides a list of security advisories. Either a list of packages as query or request parameter or a timestamp as updatedSince query parameter need to be passed.
When querying with a list of packages, known packages will be included in the response with an empty array if they don't have any listed vulnerability. Package names which are not known to have no vulnerability will not be included at all to show that we do not have data on those.
GET https://packagist.org/api/security-advisories/?updatedSince=[timestamp]&packages[]=[vendor/package]
{
"advisories": {
"[vendor]/[package]": [
{
"advisoryId": "[unique id]",
"packageName": "[vendor]/[package]",
"remoteId": "[deprecated, use sources instead]",
"title": "[title]",
"link": "[url to issue disclosure]",
"cve": "[cve if available]",
"affectedVersions": [affected version in form of a composer constraint]",
"source": "[deprecated, use sources instead]",
"sources": [
{
"name": "[Name of the source where the advisory was found e.g. GitHub or FriendsOfPHP/security-advisories]"
"remoteId": "[A reference to identify the advisory in the source e.g. the GitHub advisory id]"
}
],
"reportedAt": "[date the issue was reported]",
"composerRepository": "[composer repository the package can be found in]",
"severity": [severity if available, following the CVSS3 spec]
}
]
}
}
Working example: https://packagist.org/api/security-advisories/?packages[]=monolog/monolog
Create a package
This endpoint creates a package for a specific repo. Parameters username
and apiToken
are required. Only the POST
method is allowed. The content-type: application/json
header is required.
This endpoint is considered UNSAFE and requires your main API token to be used.
POST https://packagist.org/api/create-package?username=[username]&apiToken=[apiToken] {"repository":"[url]"}
{
"status": "success"
}
Working example:
curl -X POST -H'Content-Type:application/json' 'https://packagist.org/api/create-package?username=USERNAME&apiToken=********' -d '{"repository":"https://github.com/Seldaek/monolog"}'
Edit a package
This endpoint allows you to edit a package URL. Parameters username
and apiToken
are required. Only the PUT
method is allowed. The content-type: application/json
header is required.
This endpoint is considered UNSAFE and requires your main API token to be used.
PUT https://packagist.org/api/packages/[package name]?username=[username]&apiToken=[apiToken] {"repository":"[url]"}
{
"status": "success"
}
Working example:
curl -X POST -H'Content-Type:application/json' 'https://packagist.org/api/packages/monolog/monolog?username=USERNAME&apiToken=********' -d '{"repository":"https://github.com/Seldaek/monolog"}'
Update a package
This endpoint updates a package by canonical repository URL or packagist.org package URL. Parameters username
and apiToken
are required. Only the POST
method is allowed. The content-type: application/json
header is required.
This endpoint is considered SAFE and allows either your main or safe API token to be used.
POST https://packagist.org/api/update-package?username=[username]&apiToken=[apiToken] {"repository":"[url]"}
{
"status": "success",
"jobs": ["job-id", ".."]
}
Working examples:
curl -X POST -H'Content-Type:application/json' 'https://packagist.org/api/update-package?username=USERNAME&apiToken=********' -d '{"repository":"https://github.com/Seldaek/monolog"}'
curl -X POST -H'Content-Type:application/json' 'https://packagist.org/api/update-package?username=USERNAME&apiToken=********' -d '{"repository":"https://packagist.org/monolog/monolog"}'