How to get all drafts data using content API?

I am using ghost latest version and i am trying to preview draft urls in next js 13 app router (draft mode).

Api query which i am using is

{{hosturl}}/api/content/posts/?key={{API_KEY}}&limit=all&filter=status:draft&fields=uuid

which gives all the published post uuid. Is there anything that i am missing?

Draft content is not available via the Content API otherwise all your drafts would be available publicly. You need to use the Admin API if you want to fetch drafts.

2 Likes

Hi @Kevin Thank you for answering. So problem with the admin API is Authentication.

So i want to access and preview the draft URL for my own blogs. I am consuming ghost content API for that but for the draft preview i have to consume admin url and which giives me 403. How can someone authenticate admin api via fetch. I am using Next js 13.4 app router.

Thanks in advance.

Infact @Kevin I am also facing the same issue in my case I want to access the draft on the production just using the API keys where I dont need additional authentication, in my knowledge ghost provides 3 types and API key is one of them, but I cant access it directly using the admin api

{{hosturl}}/api/admin/posts/?key={{admin_key}}&include=tags,authors&limit=all&filter=uuid:{{uuid}}

It gives me this response

{
  errors: [
    {
      message: 'Authorization failed',
      context: 'Unable to determine the authenticated user or integration. Check that cookies are being passed through if using session authentication.',
      type: 'NoPermissionError',
      details: null,
      property: null,
      help: null,
      code: null,
      id: '90bee4b0-bf5c-11ee-9bfb-7f6f535c7e92',
      ghostErrorCode: null
    }
  ]
}

which should not be the case when I use directly keys provided on the ghost app. any workaround for the same issue?

You’re making a get request? That’s the problem. You’ll need to post with an authorization token. See this document for an example.

I recommend using the JavaScript client.

1 Like

thanks @Cathy_Sarisky I will surely try this out and update here

Hey @Cathy_Sarisky I tried the same what it is written in the Documentation still It gives the same error Which I have posted above any workaround do lmk.
Thanks in advance!!

Post your code, minus any keys.

Hi!
This link:
https://url.pl/ghost/api/admin/posts/?key=yourkey&filter=status:draft
Works with posts that are drafts.

Hey @Cathy_Sarisky

const api = new GhostAdminAPI({
  url: host,
  version: 'v5.0',
  key: admin_key,
});
const getPostContent = (slug) => {
  return api.posts
    .read({ id: slug })
    .then((response) => {
      console.log("Response:", response);
      return response; 
    })
    .catch((error) => {
      throw error; 
    });
};

this is my admin access code

@AnimaVillis well it throws an auth error stated above thanks for your effort :)

Are You sure You use it in right way?
This anorch works fine, give full script without admin_key then I’ll check.

This does not work, since you cannot pass an admin API key through a key query parameter.

There are three ways to authenticate with the admin API. Cathy linked to the documentation above.

Opening the URL in a browser will work, however, when you’re logged in to your Ghost admin, since your browser then has an authentication cookie. So, that’s potentially why it looks like it works in your browser. You are implicitly using one of the three authentication methods. That has nothing to do with the key parameter though.

So, the auth error @Pratyush_Mahapatra mentions is expected. To get around that, however, you can simply pass the admin API key in your request headers.

Here are two API examples I just tested with the admin SDK:

To browse all draft posts:

const api = new GhostAdminAPI({
  url: host,
  version: 'v5.0',
  key: admin_key,
});

const getDraftPosts = async () => {
    return await api.posts.browse({
        filter: 'status:draft',
    });
}

To then get an individual post:

const api = new GhostAdminAPI({
  url: host,
  version: 'v5.0',
  key: admin_key,
});

const getPost = async (id) => {
    return await api.posts.read({
        id
    });
}

@Keval_Rathod this should also answer your question from yesterday. If you want to use fetch directly instead of the sdk, you just have to pass your admin token to the Authorization header.

2 Likes


So dont tell me somethink isn’t working when I check it.

Clarification: you can’t just send your admin token, you need to create a JWT and send that in the auth header

1 Like

As @jannis mentioned

The OP is trying to programmatically use the APi, so unlikely to use session auth.

3 Likes

Okay, sorry my bad i’ve checked what are You exactly talking about.

@Pratyush_Mahapatra @Keval_Rathod
Sorry for other language but this code;

<?php
$KEY = 'adminkey';

list($ID, $SECRET) = explode(':', $KEY);

$NOW = time();
$FIVE_MINS = $NOW + 300;
$HEADER = '{"alg": "HS256","typ": "JWT", "kid": "' . $ID . '"}';
$PAYLOAD = '{"iat":' . $NOW . ',"exp":' . $FIVE_MINS . ',"aud": "/admin/"}';

function base64_url_encode($input) {
    $base64 = base64_encode($input);
    $base64_url = strtr($base64, '+/', '-_');
    return rtrim($base64_url, '=');
}

$header_base64 = base64_url_encode($HEADER);
$payload_base64 = base64_url_encode($PAYLOAD);
$header_payload = $header_base64 . '.' . $payload_base64;

$signature = base64_url_encode(hash_hmac('sha256', $header_payload, hex2bin($SECRET), true));

$TOKEN = $header_payload . '.' . $signature;

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => "https://yoururl.com/ghost/api/admin/posts/?filter=status:draft",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => array(
        "Authorization: Ghost " . $TOKEN,
        "Content-Type: application/json",
        "Accept-Version: v3.0"
    ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
    echo "Error: " . $err;
} else {
    echo "Response: " . $response;
}

?>

Give this result:


Sooo you need to rewrite it for js.

The JS SDK does this behind the scenes, I think @jannis’s code suggestions are a drop-in solution to the stated issues.

1 Like