PHPackages                             williamsdb/php2bluesky - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. williamsdb/php2bluesky

ActiveLibrary[API Development](/categories/api)

williamsdb/php2bluesky
======================

A library that makes posting to Bluesky from PHP simple.

v2.3.12(1mo ago)19578↑60%5[2 issues](https://github.com/williamsdb/php2Bluesky/issues)GPL-3.0-or-laterPHPPHP ^8.0

Since Jan 5Pushed 1mo ago4 watchersCompare

[ Source](https://github.com/williamsdb/php2Bluesky)[ Packagist](https://packagist.org/packages/williamsdb/php2bluesky)[ Docs](https://php2Bluesky.dev)[ RSS](/packages/williamsdb-php2bluesky/feed)WikiDiscussions main Synced 2d ago

READMEChangelogDependencies (3)Versions (34)Used By (0)

### php2Bluesky

[](#php2bluesky)

 A simple library that allows posting to Bluesky via the API.

 Table of Contents1. [About The Project](#about-the-project)
    - [Built With](#built-with)
2. [Getting Started](#getting-started)
    - [Prerequisites](#prerequisites)
    - [Installation](#installation)
3. [Usage](#usage)
4. [Roadmap](#roadmap)
5. [Contributing](#contributing)
6. [License](#license)
7. [Contact](#contact)
8. [Acknowledgments](#acknowledgments)

About The Project
-----------------

[](#about-the-project)

With all the uncertainty surrounding the future of X (née Twitter), I decided to take a look at Bluesky, which somewhat ironically has its roots in Twitter, where it was started as an internal project. I worry about Bluesky's long-term, given that ultimately it too has to make money, something that Twitter has singularly failed to do. None of this, of course, affects the topic today, which is posting to Bluesky via the API.

I needed a way to post to Bluesky from PHP and so I searched for a library to help and when I couldn't find one I wrote this.

[![Buy Me a Coffee at ko-fi.com](https://camo.githubusercontent.com/8d3dd9481705944be1335f13e318f077e8447744c3a30dea623345e711906720/68747470733a2f2f73746f726167652e6b6f2d66692e636f6d2f63646e2f6b6f6669352e706e673f763d36)](https://ko-fi.com/Y8Y0POEES)

([back to top](#readme-top))

### Built With

[](#built-with)

- [PHP](https://php.net)
- [BlueskyApi by Clark Rasmussen](https://github.com/cjrasmussen/BlueskyApi)

([back to top](#readme-top))

Getting Started
---------------

[](#getting-started)

***NOTE:*** This is for v2 of php2Bluesky. If you are looking for the v1 details you can find that [here](https://github.com/williamsdb/php2Bluesky/blob/8b137617bda0bd9dd97462966a8d9404e08ea807/readme.md).

Running the script is very straightforward:

1. install [composer](https://getcomposer.org/)
2. add the BlueskyAPI

> composer.phar require cjrasmussen/bluesky-api

3. add php2Bluesky

> composer.phar require williamsdb/php2bluesky

Now you can inspect [example.php](https://github.com/williamsdb/php2Bluesky/blob/main/src/example.php) to get some examples and/or see below.

If you are interested in what is happening under the hood then read [this series of blog posts](https://www.spokenlikeageek.com/tag/bluesky/).

### Prerequisites

[](#prerequisites)

Requirements are very simple, it requires the following:

1. PHP (I tested on v8.1.13) - requires php-dom and php-gd
2. Clark Rasmussen's [BlueskyApi](https://github.com/cjrasmussen/BlueskyApi) (requires v2 or above)
3. a Bluesky account and an Application Password (see [this blog post](https://www.spokenlikeageek.com/2023/11/06/posting-to-bluesky-via-the-api-from-php-part-one/) for details of how to do that)
4. [ffprobe](https://ffmpeg.org/ffprobe.html) if you intend to upload videos or gifs (optional)
5. [ffmpeg](https://ffmpeg.org/ffmpeg.html) if you intend to upload videos or gifs (optional).

Read more about the [requirements for video upload here](https://www.spokenlikeageek.com/2025/05/10/uploading-videos-with-php2bluesky/).

### Installation

[](#installation)

1. As above

([back to top](#readme-top))

Usage
-----

[](#usage)

Here's a few examples to get you started.

Note: connection to the Bluesky API is made via Clark Rasmussen's [BlueskyApi](https://github.com/cjrasmussen/BlueskyApi) which this makes a connection to Bluesky and manages tokens etc. See [here](https://github.com/cjrasmussen/BlueskyApi) for more details.

### Setup and connect to Bluesky

[](#setup-and-connect-to-bluesky)

```
require __DIR__ . '/vendor/autoload.php';

use williamsdb\php2bluesky\php2Bluesky;

$php2Bluesky = new php2Bluesky();

$handle = 'yourhandle.bsky.social';
$password = 'abcd-efgh-ijkl-mnop';

// connect to Bluesky API
$connection = $php2Bluesky->bluesky_connect($handle, $password);
```

#### Overriding the defaults

[](#overriding-the-defaults)

When you instantiate php2Bluesky a number of defaults are set as shown in the table below. However, you can override these as follows:

```
$php2Bluesky = new php2Bluesky($linkCardFallback = 'RANDOM',
                               $failOverMaxPostSize = FALSE,
                               $randomImageURL = 'https://picsum.photos/1024/536',
                               $fileUploadDir='/tmp',
                               $defaultLang = ['fr'],
                               $autoRotate = false);
```

See details below on how to set these.

NameDefault valuesExplanation$linkCardFallback'BLANK'What should happen if a linkcard doesn't have any associated image.

Possibe values:

RANDOM - a random image taken from $randomImageURL
BLANK - a blank image
ERROR - throw an error
URL - the image at this location, URL=https://...$failOverMaxPostSizeFALSEThrow an error if the text is longer than the allowed length of a post.$randomImageURL''Where to source the random image. Use with $linkCardFallback above.$fileUploadDir'/tmp'Where to upload images to before posting them. Make sure that the process has permissions to write here.$defaultLang\['en'\]The default languages of posts. This must be specified as an array.$autoRotatetrueA boolean indicating whether images should be auto rotated based on their EXIF data.### Sending text with tags

[](#sending-text-with-tags)

```
$text = "This is some text with a #hashtag.";

$response = $php2Bluesky->post_to_bluesky($connection, $text);
print_r($response);
if (!isset($response->error)){
    $url = $php2Bluesky->permalink_from_response($response, $handle);
    echo $url.PHP_EOL;
}
```

### Uploading a post with a single image and embedded url

[](#uploading-a-post-with-a-single-image-and-embedded-url)

```
$filename1 = 'https://upload.wikimedia.org/wikipedia/en/6/67/Bluesky_User_Profile.png';
$text = 'Screenshot of Bluesky';
$alt = 'This is the screenshot that Wikipedia uses for their https://en.wikipedia.org/wiki/Bluesky entry.';

$response = $php2Bluesky->post_to_bluesky($connection, $text, $filename1, '', $alt);
print_r($response);
if (!isset($response->error)){
    $url = $php2Bluesky->permalink_from_response($response, $handle);
    echo $url.PHP_EOL;
}
```

### Uploading a post with multiple images (both local and remote)

[](#uploading-a-post-with-multiple-images-both-local-and-remote)

```
$filename1 = 'https://upload.wikimedia.org/wikipedia/en/6/67/Bluesky_User_Profile.png';
$filename2 = '/Users/neilthompson/Development/php2Bluesky/Screenshot1.png';
$filename3 = 'https://www.spokenlikeageek.com/wp-content/uploads/2024/11/2024-11-18-19-28-59.png';
$filename4 = '/Users/neilthompson/Development/php2Bluesky/Screenshot2.png';
$text = 'An example of four images taken both from a local machine and remote locations with some alt tags';

// send multiple images with text
$imageArray = array($filename1, $filename2, $filename3, $filename4);
$alt = array('this has an alt', 'so does this');

$response = $php2Bluesky->post_to_bluesky($connection, $text, $imageArray, '', $alt);
print_r($response);
if (!isset($response->error)){
    $url = $php2Bluesky->permalink_from_response($response, $handle);
    echo $url.PHP_EOL;
}
```

### Uploading a post with a single video

[](#uploading-a-post-with-a-single-video)

```
$filename1 = '/path/to/local/video.mp4';
$text = 'A beautiful video';

$response = $php2Bluesky->post_to_bluesky($connection, $text, $filename1);
print_r($response);
if (!isset($response->error)){
    $url = $php2Bluesky->permalink_from_response($response, $handle);
    echo $url.PHP_EOL;
}
```

### Adding labels

[](#adding-labels)

Bluesky allows certain lables to be applied to posts to indicate whether they include any graphic content. There is a table below of what labels are supported but also see [BlueskyConsts.php](https://github.com/williamsdb/php2Bluesky/blob/main/src/BlueskyConsts.php) for the latest.

```
$response = $php2Bluesky->post_to_bluesky(connection: $connection,
                                          text: $text = "this is the text of your post",
                                          media: $media = "https://www.spokenlikeageek.com/wp-content/uploads/2025/06/SCR-20250628-jzfa.png",
                                          link: $link = '',
                                          alt: $alt = "Image of labels applied to a Bluesky post",
                                          labels: $labels = ["!no-unauthenticated", "porn", "sexual"]);
```

Label ValueUI LabelExplanationpornAdultExplicit/erotic sexual contentsexualSuggestiveMild or suggestive sexual themesnudityNudityNon‑erotic or artistic nuditygraphic-mediaGraphic MediaViolent or graphic content!no-unauthenticatedn/amakes the content inaccessible to logged-out users in applications which respect the label.### Setting the language

[](#setting-the-language)

By default the language of your posts is set to English but you can override this in one of two ways. Either specify the langauge when creating the instance of php2Bluesky (see above) or pass it when posting. It must be an array even if you are passing a single language.

```
$response = $php2Bluesky->post_to_bluesky(connection: $connection,
                                          text: $text = "Bonjour le monde!",
                                          media: $media = "",
                                          link: $link = "",
                                          alt: $alt = "",
                                          labels: $labels = "",
                                          lang: $lang = ["fr"]);
```

It is also possible to pass multiple languages as follows:

```
$response = $php2Bluesky->post_to_bluesky(connection: $connection,
                                          text: $text = "Bonjour le monde!".PHP_EOL."Hello World!",
                                          media: $media = "",
                                          link: $link = "",
                                          alt: $alt = "",
                                          labels: $labels = ""
                                          lang: $lang = ["fr", "en-GB"]);
```

### Getting current rate limits

[](#getting-current-rate-limits)

Once you have a connection created, you can find out what your rate limit is, how many messages you can send, and when the rate limit resets. Use this to ensure that your messages don't get rejected.

```
$rates = $php2Bluesky->get_rate_limits($connection);
print_r($rates);
```

The above returns an array as follows:

```
Array
(
    [Limit] => 10
    [Remaining] => 9
    [Reset] => 1776977065
    [Reset_Human] => 2026-04-23 20:44:25
)

```

### Setting who can reply to a post

[](#setting-who-can-reply-to-a-post)

##### v2.3.11 and above

[](#v2311-and-above)

With this call, you are able to set who is able to reply to a post. Unlike the other calls, you can ONLY do this after posting.

```
$text = "This is some text to which only @spokenlikeageek.com can reply.";

$response = $php2Bluesky->post_to_bluesky($connection, $text);

$php2Bluesky->add_restrictions($connection, $response, 'Mention');
```

You can send up to five allow values as an array, i.e `['mention', 'follower']`

Allow ValueExplanationmentionAnyone mentioned in the postfollowingAnyone you are followingfollowerAnyone who is a followerlistAnyone in this list...
NOT CURRENTLY SUPPORTED\[\]Empty array - nobody can reply### Error Codes

[](#error-codes)

List of error codes and messages that are thrown by php2Bluesky.

MessageCodeBLANK specified for fallback image but blank.png is missing.1001Could not determine mime type of file.1002File type not supported: ``1003Could not get the size of the image.1004Provided text greater than ``1005Error loading url: ``1006No suitable image found for link card.1007Video duration exceeds maximum allowed duration of `` seconds.1009Failed to fetch remote file: `` - ``1010No post id found.1011UNUSED1012Local file does not exist: ``1013Could not create image resource for resizing1014Unsupported label: ``1015FFprobe failed (code ``): ``1016Failed to parse ffprobe JSON: ``1017Unable to determine video duration.1018FFmpeg not found in the local directory or globally.1019FFmpeg failed (code ``): ``1020Output file missing or empty. FFmpeg output: ``1021FFprobe not found in the local directory or globally.1022Unsupported Threadgate: ``1023([back to top](#readme-top))

Known Issues
------------

[](#known-issues)

See the [open issues](https://github.com/williamsdb/php2Bluesky/issues) for a full list of proposed features (and known issues).

([back to top](#readme-top))

Contributing
------------

[](#contributing)

Thanks to the follow who have provided techincal and/or financial support for the project:

- [Jan Strohschein](https://bsky.app/profile/hayglow.bsky.social)
- [Ludwig Noujarret](https://bsky.app/profile/ludwig.noujarret.com)
- [Paul Lee](https://bsky.app/profile/drpaullee.bsky.social)
- [AJ](https://bsky.app/profile/asjmcguire.bsky.social)
- [Boba Fett Fan Club](https://bsky.app/profile/bobafettfanclub.com)
- [Doug "Bear" Hazard](https://bsky.app/profile/bearlydoug.com)
- [Muzikals](https://bsky.app/profile/muzikals.eurosky.social)

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request

([back to top](#readme-top))

License
-------

[](#license)

Distributed under the GNU General Public License v3.0. See `LICENSE` for more information.

([back to top](#readme-top))

Contact
-------

[](#contact)

Bluesky - [@spokenlikeageek.com](https://bsky.app/profile/spokenlikeageek.com)

Mastodon - [@spokenlikeageek](https://techhub.social/@spokenlikeageek)

X - [@spokenlikeageek](https://x.com/spokenlikeageek)

Website - [https://spokenlikeageek.com](https://www.spokenlikeageek.com/tag/bluesky/)

Project link - [Github](https://github.com/williamsdb/php2Bluesky)

([back to top](#readme-top))

Acknowledgments
---------------

[](#acknowledgments)

- [BlueskyApi](https://github.com/cjrasmussen/BlueskyApi)

([back to top](#readme-top))

[![](https://github.com/williamsdb/php2Bluesky/graphs/contributors)](https://img.shields.io/github/contributors/williamsdb/php2Bluesky.svg?style=for-the-badge)

[![](https://camo.githubusercontent.com/32a1e4463c064cef1c96040596157b9bc229781b22a015e2760ec60e9618f888/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)](https://camo.githubusercontent.com/32a1e4463c064cef1c96040596157b9bc229781b22a015e2760ec60e9618f888/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)[![](https://camo.githubusercontent.com/6859039f6cf9d93f6d6759ecc5e486a5c6d1ccc663115973916869a2c091289d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)](https://camo.githubusercontent.com/6859039f6cf9d93f6d6759ecc5e486a5c6d1ccc663115973916869a2c091289d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)[![](https://camo.githubusercontent.com/907367fae0799b78f37878da9dec212e22cd8be2e95a4bcbbc19b0edd277c21c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)](https://camo.githubusercontent.com/907367fae0799b78f37878da9dec212e22cd8be2e95a4bcbbc19b0edd277c21c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)[![](https://camo.githubusercontent.com/558d8e9e1a9a610b99783b4e826683177d0f01db65b1153cff5be0b60d3f36e6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)](https://camo.githubusercontent.com/558d8e9e1a9a610b99783b4e826683177d0f01db65b1153cff5be0b60d3f36e6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f77696c6c69616d7364622f70687032426c7565736b792e7376673f7374796c653d666f722d7468652d6261646765)

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance87

Actively maintained with recent releases

Popularity27

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 93.4% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~15 days

Recently: every ~7 days

Total

33

Last Release

56d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b9d9ffb3ee8f41d45625f565a2ad7316ebbe947f4972002ceb640dab6276da88?d=identicon)[williamsdb](/maintainers/williamsdb)

---

Top Contributors

[![williamsdb](https://avatars.githubusercontent.com/u/8099483?v=4)](https://github.com/williamsdb "williamsdb (85 commits)")[![eiland-asbl](https://avatars.githubusercontent.com/u/5505151?v=4)](https://github.com/eiland-asbl "eiland-asbl (2 commits)")[![japafrite](https://avatars.githubusercontent.com/u/49430437?v=4)](https://github.com/japafrite "japafrite (2 commits)")[![MatthewThompson](https://avatars.githubusercontent.com/u/10958683?v=4)](https://github.com/MatthewThompson "MatthewThompson (1 commits)")[![RomanSixty](https://avatars.githubusercontent.com/u/467921?v=4)](https://github.com/RomanSixty "RomanSixty (1 commits)")

---

Tags

phpblueskyat protocol

### Embed Badge

![Health badge](/badges/williamsdb-php2bluesky/health.svg)

```
[![Health](https://phpackages.com/badges/williamsdb-php2bluesky/health.svg)](https://phpackages.com/packages/williamsdb-php2bluesky)
```

###  Alternatives

[cjrasmussen/bluesky-api

Simple helper for interacting with the Bluesky API/AT protocol

4013.9k11](/packages/cjrasmussen-bluesky-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
