> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Getting title-managed Achievements

> Sample XSAPI code to retrieve a player's title-managed XBOX services achievements list, page through results, and fetch a single achievement by ID.

This topic covers the following:

* [Before you begin](#before-you-begin)
* [Getting the achievements list](#getting-the-achievements-list)
* [Getting the next page of achievements results](#getting-the-next-page-of-achievements-results)
* [Getting a single achievement](#getting-a-single-achievement)

## Before you begin

* Your product uses the full XBOX services feature set. New products start with limited XBOX services, and achievements and gamerscore aren't available until your product is onboarded. For more information, see [Request product onboarding to enable full XBOX services](https://docs.xbox.com/home/onboarding.md#request-product-onboarding-to-enable-full-xbox-services).
* You configured your achievements in Partner Center and published the XBOX services configuration to your development sandbox. For more information, see [Configuring title-managed Achievements in Partner Center](https://docs.xbox.com/services/xbox-services/player-data/achievements/title-managed/config/live-achievements-tm-config.md).
* Your dev kit or Windows PC uses the same development sandbox, and you sign in with a test account that has access to that sandbox. For more information, see [XBOX services sandboxes overview](https://docs.xbox.com/services/xbox-services/fundamentals/sandboxes/live-setup-sandbox.md).
* Your game initialized the Gaming Runtime and then called [XblInitialize](https://docs.xbox.com/reference/live/xsapi-c/xbox_live_global_c/functions/xblinitialize.md) with an [XblInitArgs](https://docs.xbox.com/reference/live/xsapi-c/xbox_live_global_c/structs/xblinitargs.md) structure whose `scid` member contains your Service Configuration ID (SCID). The SCID is case-sensitive. For more information, see [How to use XBOX services APIs](https://docs.xbox.com/services/xbox-services/fundamentals/xbox-services-api/live-gs-xbl-apis.md).
* You signed in a user and created an `XblContextHandle` for that user by calling [XblContextCreateHandle](https://docs.xbox.com/reference/live/xsapi-c/xbox_live_context_c/functions/xblcontextcreatehandle.md).

## Getting the achievements list

To get the achievements for the running title, call [XblAchievementsGetAchievementsForTitleIdAsync](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementsfortitleidasync.md), as follows.

### Calling XblAchievementsGetAchievementsForTitleIdAsync

**C++**

```cpp theme={null}
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of XAsyncBlock*.

    XblAchievementsResultHandle resultHandle;
    auto hr = XblAchievementsGetAchievementsForTitleIdResult(asyncBlock, &resultHandle);

    if (SUCCEEDED(hr))
    {
        const XblAchievement* achievements = nullptr;
        size_t achievementsCount = 0;
        hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount);
        if (SUCCEEDED(hr))
        {
            for (size_t i = 0; i < achievementsCount; i++)
            {
                LogToScreen("Achievement %s: %s = %s", 
                    achievements[i].id, 
                    achievements[i].name,
                    (achievements[i].progressState == XblAchievementProgressState::Achieved) ? "Achieved" : "Not achieved");
            }
        }

        XblAchievementsResultCloseHandle(resultHandle); // When you're done with the handle, close it.
        achievements = nullptr; // Clear the array after calling XblAchievementsResultCloseHandle to avoid the pointer to freed memory.
        // Instead, you could keep the handle open and store it, for example to get the next page of results.
        // If you need another reference to the handle, call XblAchievementsResultDuplicateHandle().
    }
};

HRESULT hr = XblAchievementsGetAchievementsForTitleIdAsync(
    xboxLiveContext,
    xboxUserId,
    titleId,
    achievementType,
    unlockedOnly,
    orderBy,
    skipItems,
    maxItems,
    asyncBlock.get()
);
if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* because the callback will take over ownership.
    // If the call fails, std::unique_ptr will keep ownership and delete XAsyncBlock*.
    asyncBlock.release(); 
}
```

For more information, see the following:

* [XAsyncBlock](https://docs.xbox.com/reference/system/xasync/structs/xasyncblock.md)
* [XblAchievement](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/structs/xblachievement.md)
* [XblAchievementProgressState](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/enums/xblachievementprogressstate.md)
* [XblAchievementsGetAchievementsForTitleIdAsync](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementsfortitleidasync.md)
* [XblAchievementsGetAchievementsForTitleIdResult](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementsfortitleidresult.md)
* [XblAchievementsResultCloseHandle](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultclosehandle.md)
* [XblAchievementsResultDuplicateHandle](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultduplicatehandle.md)
* [XblAchievementsResultGetAchievements](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetachievements.md)

[Return to the top of this topic.](#top)

## Getting the next page of achievements results

There might be more pages of achievements. To check, call [XblAchievementsResultHasNext](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresulthasnext.md). To get the next page, call [XblAchievementsResultGetNextAsync](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextasync.md) with the current result handle, and then call [XblAchievementsResultGetNextResult](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextresult.md) in the callback.

`XblAchievementsResultGetNextResult` returns a new result handle. Each result handle must be closed separately. Passing 0 for `maxItems` attempts to retrieve all items, but the service can provide a default page size and can return fewer items than requested even when more pages remain. Always check `XblAchievementsResultHasNext` instead of assuming that one call returns every achievement.

### Processing every page of results

In the following example, instead of closing the result handle from `XblAchievementsGetAchievementsForTitleIdResult`, pass it to `ProcessAchievementsPage`. The function takes ownership of the handle that you pass to it and closes that handle exactly once. The current handle stays open until the request for the next page completes.

**C++**

```cpp theme={null}
void ProcessAchievementsPage(XblAchievementsResultHandle resultHandle, XTaskQueueHandle queue, uint32_t maxItems)
{
    const XblAchievement* achievements = nullptr;
    size_t achievementsCount = 0;
    HRESULT hr = XblAchievementsResultGetAchievements(resultHandle, &achievements, &achievementsCount);
    if (SUCCEEDED(hr))
    {
        for (size_t i = 0; i < achievementsCount; i++)
        {
            LogToScreen("Achievement %s: %s = %s",
                achievements[i].id,
                achievements[i].name,
                (achievements[i].progressState == XblAchievementProgressState::Achieved) ? "Achieved" : "Not achieved");
        }
    }

    bool hasNext = false;
    if (SUCCEEDED(hr))
    {
        hr = XblAchievementsResultHasNext(resultHandle, &hasNext);
    }

    if (FAILED(hr) || !hasNext)
    {
        // This is the last page, or an error occurred. Close the handle.
        XblAchievementsResultCloseHandle(resultHandle);
        return;
    }

    struct NextPageContext
    {
        XblAchievementsResultHandle currentHandle;
        uint32_t maxItems;
    };

    auto asyncBlock = std::make_unique<XAsyncBlock>();
    asyncBlock->queue = queue;
    asyncBlock->context = new NextPageContext{ resultHandle, maxItems };
    asyncBlock->callback = [](XAsyncBlock* asyncBlock)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of XAsyncBlock*.
        std::unique_ptr<NextPageContext> context{ static_cast<NextPageContext*>(asyncBlock->context) };

        XblAchievementsResultHandle nextHandle = nullptr;
        HRESULT hr = XblAchievementsResultGetNextResult(asyncBlock, &nextHandle);

        // The request for the next page is complete, so close the handle for the previous page.
        XblAchievementsResultCloseHandle(context->currentHandle);

        if (SUCCEEDED(hr))
        {
            // Process the new page. ProcessAchievementsPage closes nextHandle.
            ProcessAchievementsPage(nextHandle, asyncBlock->queue, context->maxItems);
        }
        else
        {
            LogToScreen("XblAchievementsResultGetNextResult failed: 0x%08X", static_cast<unsigned int>(hr));
        }
    };

    hr = XblAchievementsResultGetNextAsync(resultHandle, maxItems, asyncBlock.get());
    if (SUCCEEDED(hr))
    {
        // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* because the callback will take over ownership.
        asyncBlock.release();
    }
    else
    {
        // The callback won't run, so clean up here.
        delete static_cast<NextPageContext*>(asyncBlock->context);
        XblAchievementsResultCloseHandle(resultHandle);
    }
}
```

For more information, see the following:

* [XAsyncBlock](https://docs.xbox.com/reference/system/xasync/structs/xasyncblock.md)
* [XblAchievement](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/structs/xblachievement.md)
* [XblAchievementProgressState](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/enums/xblachievementprogressstate.md)
* [XblAchievementsResultCloseHandle](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultclosehandle.md)
* [XblAchievementsResultGetAchievements](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetachievements.md)
* [XblAchievementsResultGetNextAsync](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextasync.md)
* [XblAchievementsResultGetNextResult](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetnextresult.md)
* [XblAchievementsResultHasNext](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresulthasnext.md)

[Return to the top of this topic.](#top)

## Getting a single achievement

To get a single achievement, call [XblAchievementsGetAchievementAsync](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementasync.md), as follows.

**Flat C API**

```cpp theme={null}
auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of XAsyncBlock*.
    XblAchievementsResultHandle resultHandle;
    auto hr = XblAchievementsGetAchievementResult(asyncBlock, &resultHandle);

    if (SUCCEEDED(hr))
    {
        const XblAchievement* achievements = nullptr;
        size_t achievementsCount = 0;
        hr = XblAchievementsResultGetAchievements( resultHandle, &achievements, &achievementsCount );

        // Use the achievements array to read the achievement data.

        XblAchievementsResultCloseHandle(resultHandle); // When you're done with the handle, close it.
        achievements = nullptr; // Clear the array after calling XblAchievementsResultCloseHandle to avoid the pointer to freed memory.
    }
};

HRESULT hr = XblAchievementsGetAchievementAsync(
    xboxLiveContext,
    xboxUserId,
    scid,
    achievementId.c_str(),
    asyncBlock.get()
);
if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* because the callback will take over ownership.
    // If the call fails, std::unique_ptr will keep ownership and delete XAsyncBlock*.
    asyncBlock.release();
}
```

For more information, see the following:

* [XAsyncBlock](https://docs.xbox.com/reference/system/xasync/structs/xasyncblock.md)
* [XblAchievement](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/structs/xblachievement.md)
* [XblAchievementsGetAchievementAsync](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementasync.md)
* [XblAchievementsGetAchievementResult](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsgetachievementresult.md)
* [XblAchievementsResultCloseHandle](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultclosehandle.md)
* [XblAchievementsResultGetAchievements](https://docs.xbox.com/reference/live/xsapi-c/achievements_c/functions/xblachievementsresultgetachievements.md)

[Return to the top of this topic.](#top)


## Related topics

- [Title-managed achievements examples](https://docs.xbox.com/services/xbox-services/player-data/achievements/title-managed/how-to/index.md)
- [Title-managed Achievements example code (contents)](https://docs.xbox.com/services/xbox-services/player-data/achievements/title-managed/how-to/live-achievements-howto-nav.md)
- [Stats and achievements](https://docs.xbox.com/build/steam-porting-guide/features/stats-and-achievements.md)
- [Title-managed achievements](https://docs.xbox.com/services/xbox-services/player-data/achievements/title-managed/index.md)
- [Title-managed Achievements (contents)](https://docs.xbox.com/services/xbox-services/player-data/achievements/title-managed/live-achievements-tm-nav.md)
