# Quickstart (/docs/quickstart)

When you finish this page, you have a published post on one of your social accounts and its link on the platform. It takes an API key, one connected account and two API calls.

### Create an API Key [#create-an-api-key]

Open **API Keys** in the dashboard and create a key with the `read_write` permission. Copy it now, because it is shown only once.

```bash
export ONEPOSTLY_API_KEY="op_YOUR_KEY"
```

Every sample in these docs reads the key from `ONEPOSTLY_API_KEY`. See [Authentication](/docs/authentication) for headers and permissions.

### Install an SDK [#install-an-sdk]

**TypeScript**

```bash
npm install @onepostly/sdk
```

**Python**

```bash
pip install onepostly
```

Prefer plain HTTP? Skip this step. Every sample has a cURL tab, and the base URL is `https://api.onepostly.com`.

### Connect an Account [#connect-an-account]

Open **Connections** in the dashboard and sign in to a platform. Your first 2 accounts are free.

Then list your accounts to get their ids.

**TypeScript**

```typescript
import { Configuration, AccountsApi } from "@onepostly/sdk";

const accountsApi = new AccountsApi(
  new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);

const response = await accountsApi.listAccounts();
console.log(response);
```

**Python**

```python
import asyncio
import os

from onepostly import ApiClient, Configuration
from onepostly.api.accounts_api import AccountsApi

async def main() -> None:
    configuration = Configuration(access_token=os.environ["ONEPOSTLY_API_KEY"])
    async with ApiClient(configuration) as api_client:
        accounts_api = AccountsApi(api_client)
        response = await accounts_api.list_accounts()
        print(response)

asyncio.run(main())
```

**cURL**

```bash
curl -X GET "https://api.onepostly.com/v1/accounts" \
  -H "Authorization: Bearer $ONEPOSTLY_API_KEY"
```

**Response**

```json
{
  "data": [
    {
      "id": "acc_1699f4157fb643f49d2ac447491f32a8",
      "platform": "linkedin",
      "displayName": "Your Brand",
      "handle": "Your Brand",
      "avatarUrl": "https://cdn.onepostly.com/avatars/yourbrand.jpg",
      "status": "active",
      "canAutoRenew": false,
      "tokenExpiresAt": "2027-05-14T10:00:00.000Z",
      "profile": "Default",
      "metadata": {},
      "createdAt": "2027-03-01T10:00:00.000Z",
      "updatedAt": "2027-03-01T10:00:00.000Z"
    },
    {
      "id": "acc_1699f4157fb643f49d2ac447491f3202",
      "platform": "x",
      "displayName": "Your Brand",
      "handle": "yourbrand",
      "avatarUrl": "https://cdn.onepostly.com/avatars/yourbrand.jpg",
      "status": "active",
      "canAutoRenew": true,
      "tokenExpiresAt": "2027-05-14T10:00:00.000Z",
      "profile": "Default",
      "metadata": {},
      "xSubscriptionType": "None",
      "createdAt": "2027-02-28T10:00:00.000Z",
      "updatedAt": "2027-02-28T10:00:00.000Z"
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
```

You publish to an account by its `id`, such as `acc_1699f4157fb643f49d2ac447491f32a8`. Its `status` must be `active`.

### Publish a Post [#publish-a-post]

Send the text and the account to publish to.

**TypeScript**

```typescript
import { Configuration, PostsApi } from "@onepostly/sdk";

const postsApi = new PostsApi(
  new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);

const response = await postsApi.createPost({
  createPostBody: {
    "text": "Hello from Onepostly.",
    "accounts": [
      {
        "accountId": "acc_YOUR_LINKEDIN_ACCOUNT_ID"
      }
    ]
  },
});
console.log(response);
```

**Python**

```python
import asyncio
import os

from onepostly import ApiClient, Configuration
from onepostly.api.posts_api import PostsApi

async def main() -> None:
    configuration = Configuration(access_token=os.environ["ONEPOSTLY_API_KEY"])
    async with ApiClient(configuration) as api_client:
        posts_api = PostsApi(api_client)
        response = await posts_api.create_post(
            create_post_body={
                "text": "Hello from Onepostly.",
                "accounts": [
                    {
                        "accountId": "acc_YOUR_LINKEDIN_ACCOUNT_ID"
                    }
                ]
            },
        )
        print(response)

asyncio.run(main())
```

**cURL**

```bash
curl -X POST "https://api.onepostly.com/v1/posts" \
  -H "Authorization: Bearer $ONEPOSTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Hello from Onepostly.",
  "accounts": [
    {
      "accountId": "acc_YOUR_LINKEDIN_ACCOUNT_ID"
    }
  ]
}'
```

**Response**

```json
{
  "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
  "data": [
    {
      "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
      "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
      "accountId": "acc_YOUR_LINKEDIN_ACCOUNT_ID",
      "platform": "linkedin",
      "source": "onepostly",
      "status": "publishing",
      "text": "Hello from Onepostly.",
      "mediaKind": "text",
      "media": [],
      "thread": [],
      "scheduledFor": null,
      "timezone": null,
      "options": {},
      "platformPostId": null,
      "url": null,
      "error": null,
      "publishedAt": null,
      "metrics": null,
      "createdAt": "2027-03-15T09:00:00.000Z",
      "updatedAt": "2027-03-15T09:00:00.000Z"
    }
  ]
}
```

You get one post per account in `data`, each with the status `publishing`.

| Platform | A post needs |
| --- | --- |
| X, Facebook, Threads, LinkedIn, Bluesky | Text or media |
| Instagram, TikTok, Pinterest | An image or a video |
| YouTube | A video |

To add media, pass a public file URL in `media`.

```json
"media": [{ "url": "https://example.com/photo.jpg" }]
```

Posts on X are paid from your [wallet](/docs/pricing#x-wallet), so top it up first.

### Check the Result [#check-the-result]

Read the post by its `id`.

**TypeScript**

```typescript
import { Configuration, PostsApi } from "@onepostly/sdk";

const postsApi = new PostsApi(
  new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);

const response = await postsApi.getPost({
  id: "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
});
console.log(response);
```

**Python**

```python
import asyncio
import os

from onepostly import ApiClient, Configuration
from onepostly.api.posts_api import PostsApi

async def main() -> None:
    configuration = Configuration(access_token=os.environ["ONEPOSTLY_API_KEY"])
    async with ApiClient(configuration) as api_client:
        posts_api = PostsApi(api_client)
        response = await posts_api.get_post(
            "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
        )
        print(response)

asyncio.run(main())
```

**cURL**

```bash
curl -X GET "https://api.onepostly.com/v1/posts/pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73" \
  -H "Authorization: Bearer $ONEPOSTLY_API_KEY"
```

**Response**

```json
{
  "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
  "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
  "accountId": "acc_YOUR_LINKEDIN_ACCOUNT_ID",
  "platform": "linkedin",
  "source": "onepostly",
  "status": "published",
  "text": "Hello from Onepostly.",
  "mediaKind": "text",
  "media": [],
  "thread": [],
  "scheduledFor": null,
  "timezone": null,
  "options": {},
  "platformPostId": "urn:li:share:7300123456789012345",
  "url": "https://www.linkedin.com/feed/update/urn:li:share:7300123456789012345",
  "error": null,
  "publishedAt": "2027-03-15T09:00:04.000Z",
  "metrics": null,
  "createdAt": "2027-03-15T09:00:00.000Z",
  "updatedAt": "2027-03-15T09:00:04.000Z"
}
```

| `status` | Means |
| --- | --- |
| `publishing` | Still on its way. Read it again shortly |
| `published` | Live, and `url` links to it on the platform |
| `failed` | Not published, and `error` says why |

Or subscribe to `post.published` and `post.failed` [webhooks](/docs/webhooks).

## If It Fails [#if-it-fails]

| What you see | Cause | Fix |
| --- | --- | --- |
| `401 UNAUTHORIZED` | The key is missing or deleted | Send the full key |
| `404 ACCOUNT_NOT_FOUND` | Wrong `accountId`, or it was disconnected | Copy it from `GET /v1/accounts` |
| `400 VALIDATION_ERROR` | A wrong field, named in `param` | Fix it, or add `media` |
| `402 INSUFFICIENT_WALLET` | The X wallet is too low | Top up in **Settings → Wallet** |
| `402 USAGE_BILLING_REQUIRED` | A 3rd account with no card | Add one in **Settings → Billing** |
| `status` is `failed` | The platform refused it | Read `error.code`, then post again |

Send support the error's `requestId`. All codes are in [Errors](/docs/error-handling).

## Next Steps [#next-steps]

- [Create Posts](/docs/publishing): Cross-post, set options per account and list your posts.

- [Media](/docs/media-uploads): Upload your own images and videos.

- [Scheduling and Editing](/docs/scheduling): Publish later, edit or cancel a scheduled post.

- [Webhooks](/docs/webhooks): Get notified when a post is published or fails.

- [Connect Accounts](/docs/connection): Connect accounts in the dashboard, or let your users connect theirs.

- [Compare Platforms](/docs/platforms): What each platform supports and how to connect it.