PHPackages                             grantholle/powerschool-api - 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. grantholle/powerschool-api

ActiveLibrary[API Development](/categories/api)

grantholle/powerschool-api
==========================

A Laravel package to make interacting with PowerSchool less painful.

4.5(2mo ago)1715.6k↓20%21MITPHPPHP ^8.3

Since Jun 26Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/grantholle/powerschool-api)[ Packagist](https://packagist.org/packages/grantholle/powerschool-api)[ RSS](/packages/grantholle-powerschool-api/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (10)Versions (60)Used By (1)

PowerSchool API
===============

[](#powerschool-api)

Taking inspiration from Laravel's database and Eloquent `Builder` class, this allows you to make api requests against PowerSchool very fluently and naturally. It handles token authentication automatically, so you can just worry about writing the requests and not the boilerplate.

This package is to be used with alongside a PowerSchool plugin that has enabled `` in the `plugin.xml`. This guide assumes you have PowerSchool API and plugin knowledge and does not cover the details of a plugin or its API.

Breaking changes for v4
-----------------------

[](#breaking-changes-for-v4)

- Requires PHP ^8.1
- Requires Laravel ^10.0

Breaking changes for v3
-----------------------

[](#breaking-changes-for-v3)

- Requires PHP ^8.0
- Requests return a new `Response` instead of `stdClass`, [see below](#responses) for details

Breaking changes for v2
-----------------------

[](#breaking-changes-for-v2)

- SSO functionality has been abstracted to a new package, [`grantholle/laravel-powerschool-auth`](https://github.com/grantholle/laravel-powerschool-auth)
- The namespace is now `GrantHolle\PowerSchool\Api`

More functionality was added in v2, along with tests for peace of mind.

Installation
------------

[](#installation)

```
composer require grantholle/powerschool-api
```

The package will be automatically discovered by Laravel, so there's no reason to add it to `config/app.php` unless you want to.

Configuration
-------------

[](#configuration)

You need to set some variables in `.env`.

```
POWERSCHOOL_ADDRESS=
POWERSCHOOL_CLIENT_ID=
POWERSCHOOL_CLIENT_SECRET=

```

Optionally, you can publish the config file to store the server address, client ID, and secret to interact with PowerSchool. This will generate `config/powerschool.php`, but is not necessary.

```
php artisan vendor:publish --provider="GrantHolle\PowerSchool\Api\PowerSchoolApiServiceProvider"
```

Debugging
---------

[](#debugging)

You can enable debugging with [Ray](https://myray.app/) that will display the raw and transformed responses for each request. This is helpful in viewing the response from PowerSchool and the `GrantHolle\PowerSchool\Api\Response` object's data. You will need to install the [Laravel package](https://spatie.be/docs/ray/v1/installation-in-your-project/laravel) and enable debugging:

```
# App debug needs to be enabled also
APP_DEBUG=true

POWERSCHOOL_DEBUG=true

```

Commands
--------

[](#commands)

```
# Fetches authorization token and caches it
php artisan powerschool:auth
```

API
---

[](#api)

Using the facade, `GrantHolle\PowerSchool\Api\Facades\PowerSchool`, you can fluently build a request for PowerSchool. By also providing several aliases to key functions, you can write requests in a way that feels comfortable to you and is easy to read. Below are examples that build on each other. See examples below to put them all together.

#### `setTable(string $table)`

[](#settablestring-table)

*Aliases: table(), forTable(), againstTable()*

This "sets" the table with which you're interacting. Applies to database extensions.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

$request = PowerSchool::table('u_my_custom_table');
```

#### `setId($id)`

[](#setidid)

*Aliases: id(), forId()*

Sets the id for a get, put, or delete request when interacting with a specific entry in the database.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

$request = PowerSchool::table('u_my_custom_table')->forId(100);
```

#### `setMethod(string $method)`

[](#setmethodstring-method)

*Aliases: usingMethod(), get(), post(), put(), patch(), delete()*

Sets the HTTP verb for the request. When using the functions `get()`, `post()`, `put()`, `patch()`, or `delete()`, the request is sent automatically.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

// This request is still not sent
$request = PowerSchool::table('u_my_custom_table')->setId(100)->method('get');

// This request is set to the get method and sent automatically
$response = PowerSchool::table('u_my_custom_table')->id(100)->get();
$response = PowerSchool::table('u_my_custom_table')->id(100)->get();
$response = PowerSchool::table('u_my_custom_table')->id(100)->delete();

// The above example could be rewritten like this...
$response = PowerSchool::table('u_my_custom_table')->id(100)->setMethod('get')->send();
```

#### `setData(Array $data)`

[](#setdataarray-data)

*Aliases: withData(), with()*

Sets the data that gets sent with requests. If it's for a custom table, you can just send the fields and their values and the structure that is compatible with PowerSchool is build automatically. If it's a named query, it's just the `args` that have been configured with the query.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

$data = [
  'column_one' => 'value',
  'column_two' => 'value',
];

// Doing "table" requests, this not getting sent
$request = PowerSchool::table('u_my_custom_table')->with($data)->method('post');

// A PowerQuery (see below)
$response = PowerSchool::pq('com.organization.product.area.name')->withData($data);
```

#### `setNamedQuery(string $query, Array $data = [])`

[](#setnamedquerystring-query-array-data--)

*Aliases: namedQuery(), powerQuery(), pq()*

The first parameter is the name of the query, following the required convention set forth by PowerSchool, `com.organization.product.area.name`. The second is the data that you may need to perform the query which has been configured in the plugin's named query xml file. If the data is included, the request is sent automatically.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

// Will not get sent
$request = PowerSchool::powerQuery('com.organization.product.area.name');

// Gets posted automatically
$response = PowerSchool::powerQuery('com.organization.product.area.name', ['schoolid' => '100']);
```

#### `setEndpoint(string $query)`

[](#setendpointstring-query)

*Aliases: toEndpoint(), to(), endpoint()*

Sets the endpoint for core PS resources.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

$requestData = [
  'students' => [
    'student' => [
      'client_uid' => 100,
      'action' => 'INSERT',
      'local_id' => 100,
      'name' => [
        'first_name' => 'John',
        'last_name' => 'Doe',
      ],
      'demographics' => [
        'gender' => 'M',
        'birth_date' => '2002-08-01',
      ],
      'school_enrollment' => [
        'entry_date' => now()->format('Y-m-d'),
        'exit_date' => now()->subDays(1)->format('Y-m-d'),
        'grade_level' => 10,
        'school_number' => 100,
      ],
    ],
  ],
];

$response = PowerSchool::toEndpoint('/ws/v1/student')->with($requestData)->post();
```

#### `q(string $expression)`

[](#qstring-expression)

*Aliases: queryExpression()*

Sets the `q` variable to the given FIQL expression.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

PowerSchool::endpoint('/ws/v1/school/3/student')
    ->q('name.last_name==Ada*')
    ->get();
```

#### `adHocFilter(string $expression)`

[](#adhocfilterstring-expression)

*Aliases: filter()*

Sets the `$q` query variable for adding ad-hoc filtering to PowerQueries.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

PowerSchool::pq('com.organization.plugin_name.entity.query_name')
    ->filter('number_column=lt=100')
    ->post();
```

#### `adHocOrder(string $expression)`

[](#adhocorderstring-expression)

*Aliases: order()*

Sets the `order` query variable for adding [ad-hoc ordering](https://support.powerschool.com/developer/#/page/powerqueries#adhoc_ordering) to PowerQueries.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

PowerSchool::pq('com.organization.plugin_name.entity.query_name')
    ->order('students.last_name,students.first_name,students.entrydate;desc')
    ->post();
```

#### `pageSize(int $pageSize)`

[](#pagesizeint-pagesize)

Sets the `pagesize` query variable.

#### `page(int $page)`

[](#pageint-page)

Sets the `page` query variable for pagination.

#### `sort(string|array $columns, bool $descending = false)`

[](#sortstringarray-columns-bool-descending--false)

Sets the `sort` and `sortdescending` variables.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

PowerSchool::pq('com.organization.plugin_name.entity.query_name')
    ->sort('column1');

// ?sort=column1&sortdescending=false
```

#### `includeCount()`

[](#includecount)

Includes the count of all the records in the results.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

PowerSchool::pq('com.pearson.core.guardian.student_guardian_detail')
    ->includeCount()
    ->post();

// {
//    "name":"Students",
//    "count":707625,
//    "record":[
//       {
//          "id":3328,
//          "name":"Students",
//          ...
//       },
//       ... Only first page of actual results returned
//    ],
//    "@extensions":"activities,u_dentistry,studentcorefields,c_studentlocator"
// }
```

#### `withQueryString(string|array $queryString)`

[](#withquerystringstringarray-querystring)

*Aliases: query()*

This will set the query string en masse rather than using the convenience methods.

#### `projection(string|array $projection)`

[](#projectionstringarray-projection)

Sets the `projection` query variable for the request.

#### `excludeProjection()`

[](#excludeprojection)

*Aliases: withoutProjection()*

Prevents the `projection` query variable from being included in the request.

#### `dataVersion(int $version, string $applicationName)`

[](#dataversionint-version-string-applicationname)

*Aliases: withDataVersion()*

Sets the `$dataversion` and `$dataversion_applicationname` data items.

#### `expansions(string|array $expansions)`

[](#expansionsstringarray-expansions)

*Aliases: withExpansions()*

Adds the `expansions` query variable.

#### `extensions(string|array $expansions)`

[](#extensionsstringarray-expansions)

*Aliases: withExtensions()*

Adds the `extensions` query variable.

Performing Requests
-------------------

[](#performing-requests)

There are many ways to perform the request after building queries. At the end of the day, each one sets the method/HTTP verb before calling `send()`. If you'd like to call `send()`, make sure you set the method by calling `method(string $verb)`. There are also helpers to set methods using constants.

```
use GrantHolle\PowerSchool\Api\RequestBuilder;

RequestBuilder::GET;
RequestBuilder::POST;
RequestBuilder::PUT;
RequestBuilder::PATCH;
RequestBuilder::DELETE;
```

#### `send()`

[](#send)

Sends the request using the verb set. By default will return the results from the query. You can also call `asJsonResponse()` prior to sending to get an instance of Laravel's `JsonResponse` class which could be returned directly to the client.

#### `count()`

[](#count)

Calling `count()` on the builder will perform a count query by appending `/count` to the end of the endpoint and perform the `get` request automatically.

#### `get(string $endpoint = null)`

[](#getstring-endpoint--null)

Sets the verb to be `get` and sends the request. You can also pass the endpoint directly to set the endpoint and perform the request automatically.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

PowerSchool::get('/ws/v1/staff/111');
```

#### `post()`

[](#post)

Sets the verb to be `post` and sends the request.

#### `put()`

[](#put)

Sets the verb to be `put` and sends the request.

#### `path()`

[](#path)

Sets the verb to be `path` and sends the request.

#### `delete()`

[](#delete)

Sets the verb to be `delete` and sends the request.

#### `getDataSubscriptionChanges(string $applicationName, int $version)`

[](#getdatasubscriptionchangesstring-applicationname-int-version)

Performs the "delta pull" for a data version subscription.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

PowerSchool::getDataSubscriptionChanges('myapp', 12345);

// {
//     "$dataversion": "16323283",
//     "tables": {
//         "userscorefields": [
//             802
//         ],
//         "users": [
//             851,
//             769,
//             802,
//             112
//         ]
//     }
// }
```

Pagination
----------

[](#pagination)

When using PowerQueries, you can easily paginate results using the `$builder->paginate($pageSize)` function. You can use this inside of a `while` loop to process all the results in your query more efficiently than returning the full result. The default page size is 100.

```
use GrantHolle\PowerSchool\Api\Facades\PowerSchool;

// PowerQuery
// You have to set data in a separate function call
// Otherwise the request will be sent automatically
$builder = PowerSchool::pq('com.organization.plugin_name.entity.query_name')
    ->with(['some_variable' => $value]);

// "Normal" endpoint
$builder = PowerSchool::to('/ws/v1/school/1/course')
    ->method(PowerSchool::GET);

// "Table" endpoints
$builder = PowerSchool::table('u_my_table')
    ->method(PowerSchool::GET);

while ($records = $builder->paginate(25)) {
    // Do something awesome
}
```

Responses
---------

[](#responses)

Prior to `v3`, API requests returned a simple `stdClass` instance containing the raw response from PowerSchool. Since `v3`, there's a new `GrantHolle\PowerSchool\Api\Response` class that gets returned, which implements `ArrayAccess`. This gives you a more ergonomic way of handling the data that gets returned from PowerSchool. Depending on the request you're making, PowerSchool returns a variety of keys and data nesting. The new `Response` class attempts to normalize the data that gets returned, making it much simpler for you to process.

Singular responses
------------------

[](#singular-responses)

Some responses are meant to return a single record, such as a response for `/ws/contacts/contact/{id}`. For these responses, the properties can be accessed as an associative array.

```
$response = PowerSchool::to('/ws/contacts/contact/123')
    ->get();

// Since Response implements ArrayAccess, we can access the attributes with keys
$response['contactId']; // 123
```

The `@extensions` and `@expansions` fields will be parsed into `$extensions` and `$expansions` properties as arrays.

```
$response->extensions;
// [
//   "personcorefields",
// ]
```

List responses
--------------

[](#list-responses)

For the responses that return a listing of results, the `Response` can be iterated using `foreach`. You don't need to worry about the property nesting, as the response will be inferred from the type of response.

```
$results = PowerSchool::to('/ws/v1/district/school')
    ->get();

 foreach ($results as $result) {
     // $result will be an array representing the school object returned
 }
```

For `get` table listings, the results are nested awkwardly. For example,

```
PowerSchool::table('u_my_table')->get();

// This returns results like
[
    [
        'id' => 1,
        'tables' => [
            'u_my_table' => [
                'column' => '',
                'column' => '',
                // etc
            ]
        ]
    ],
    // and on and on
]
```

We can reduce the awkwardness of the results by calling `squashTableResponse()` on the `Response` object.

```
PowerSchool::table('u_my_table')
    ->get()
    ->squashTableResponse();

// Now the results will be simpler
[
    [
        'column' => '',
        'column' => '',
        // etc
    ],
    // and on and on
]
```

License
-------

[](#license)

[MIT](LICENSE)

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

[](#contributing)

Thanks for taking the time to submit an issue or pull request. If it's a new feature, do your best to add a test to cover the functionality. Then run:

```
./vendor/bin/phpunit
```

###  Health Score

62

—

FairBetter than 99% of packages

Maintenance87

Actively maintained with recent releases

Popularity34

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity92

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 99% 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 ~48 days

Recently: every ~180 days

Total

59

Last Release

62d ago

Major Versions

1.4.3 → 2.0.12020-08-18

2.5.0 → 3.0.02021-10-31

3.2.8 → 4.0.02023-02-15

PHP version history (9 changes)1.0.0PHP ^7.0

1.3.1PHP ^7.2

2.2.0PHP ^7.3

2.4.0PHP ^7.4

2.4.4PHP ^7.4|^8.0

3.0.0PHP ^8.0

4.0.0PHP ^8.1

4.4.3PHP ^8.2

4.5PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/57ed974235b4a23e6aaf9d9039bff2b0d1268edc0e44ebab6e60e4bf1e6eb144?d=identicon)[grantholle](/maintainers/grantholle)

---

Top Contributors

[![grantholle](https://avatars.githubusercontent.com/u/1189456?v=4)](https://github.com/grantholle "grantholle (204 commits)")[![cars1n](https://avatars.githubusercontent.com/u/22857535?v=4)](https://github.com/cars1n "cars1n (1 commits)")[![skoontastic](https://avatars.githubusercontent.com/u/585102?v=4)](https://github.com/skoontastic "skoontastic (1 commits)")

---

Tags

hacktoberfestpowerschoolpowerschool

### Embed Badge

![Health badge](/badges/grantholle-powerschool-api/health.svg)

```
[![Health](https://phpackages.com/badges/grantholle-powerschool-api/health.svg)](https://phpackages.com/packages/grantholle-powerschool-api)
```

###  Alternatives

[skagarwal/google-places-api

Google Places Api

1913.0M8](/packages/skagarwal-google-places-api)[saloonphp/laravel-plugin

The official Laravel plugin for Saloon

805.7M125](/packages/saloonphp-laravel-plugin)[dcblogdev/laravel-microsoft-graph

A Laravel Microsoft Graph API (Office365) package

168285.5k1](/packages/dcblogdev-laravel-microsoft-graph)[vluzrmos/slack-api

Wrapper for Slack.com WEB API.

102589.1k3](/packages/vluzrmos-slack-api)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[jasara/php-amzn-selling-partner-api

A fluent interface for Amazon's Selling Partner API in PHP

1344.8k1](/packages/jasara-php-amzn-selling-partner-api)

PHPackages © 2026

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