List Changed Projects (Delta Sync)
Retrieve only the projects that have been created or modified since a given timestamp - the efficient way to keep an external system in sync with Solar Proof.
https://solarproof.com.au/apidata.php
Description
Returns a lightweight list of project IDs (with their last-modified timestamps) that have changed since
the since timestamp you supply. Feed the returned IDs into
Get Project to fetch full details for just those
projects. On a typical sync run nothing has changed and the response is a single, tiny request.
getprojectinfo for every project on a timer, one call here tells you which projects
actually changed - typically reducing hundreds of requests per run to one or two.
server_time. Store it and pass
it as since on your next call - this avoids clock-skew problems because the cursor
always comes from the Solar Proof server, never your own clock.
429 with a Retry-After header. Integrations using
this delta endpoint sit far below the limit by design.
Request Parameters
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
api_key |
string | Required | Your Solar Proof API key |
action |
string | Required | Must be set to "getprojectssince" |
since |
string | integer | Required | Return projects modified after this moment. Accepts a datetime string
(2026-07-23 09:00:00, server timezone) or unix seconds
(1784840400).Use the server_time value from your previous response.
|
page |
integer | Optional | Zero-based page (default: 0). Each page returns up to 500 projects, ordered oldest change
first. If has_more is true, request the next page. |
Response Structure
| Field | Type | Description |
|---|---|---|
status |
integer | 200 on success |
since |
string | The cutoff the server used (normalised to a datetime) |
server_time |
string | Current server time - store this and pass it as since next call |
page / per_page / count |
integer | Pagination info; count is the number of projects in this page |
has_more |
boolean | true when another page of changes exists |
projects |
array | Objects of project_id + last_modified, oldest change first |
Example Response
{
"status": 200,
"since": "2026-07-23 08:00:00",
"server_time": "2026-07-23 09:00:02",
"page": 0,
"per_page": 500,
"count": 3,
"has_more": false,
"projects": [
{ "project_id": 18244, "last_modified": "2026-07-23 08:12:41" },
{ "project_id": 18251, "last_modified": "2026-07-23 08:36:05" },
{ "project_id": 17903, "last_modified": "2026-07-23 08:58:19" }
]
}
Example Requests
Basic Request
curl -X GET "https://solarproof.com.au/apidata.php?action=getprojectssince&since=2026-07-23%2008:00:00&api_key=YOUR_API_KEY"
Full Sync Loop (Google Apps Script)
A complete incremental sync suitable for a time-driven trigger - it remembers the server-time cursor between runs and only fetches projects that changed.
function syncChangedProjects() {
var API_KEY = 'YOUR_API_KEY';
var props = PropertiesService.getScriptProperties();
var since = props.getProperty('sp_sync_cursor') || '2026-01-01 00:00:00';
var url = 'https://solarproof.com.au/apidata.php'
+ '?action=getprojectssince'
+ '&since=' + encodeURIComponent(since)
+ '&api_key=' + API_KEY;
var res = JSON.parse(UrlFetchApp.fetch(url).getContentText());
res.projects.forEach(function (p) {
// Fetch full detail ONLY for projects that actually changed
var detailUrl = 'https://solarproof.com.au/apidata.php'
+ '?action=getprojectinfo&project_id=' + p.project_id
+ '&api_key=' + API_KEY;
var detail = JSON.parse(UrlFetchApp.fetch(detailUrl).getContentText());
// ... write detail.project_info[0] into your sheet ...
});
// Store the server-side cursor for the next run (no clock skew)
props.setProperty('sp_sync_cursor', res.server_time);
}
import requests
API_KEY = 'YOUR_API_KEY'
BASE = 'https://solarproof.com.au/apidata.php'
def sync(since):
page = 0
while True:
r = requests.get(BASE, params={
'action': 'getprojectssince',
'since': since,
'page': page,
'api_key': API_KEY,
}).json()
for p in r['projects']:
detail = requests.get(BASE, params={
'action': 'getprojectinfo',
'project_id': p['project_id'],
'api_key': API_KEY,
}).json()
# ... upsert detail into your system ...
if not r['has_more']:
return r['server_time'] # store as the next 'since'
page += 1
Notes
- First call returns everything once. The change-tracking column was introduced in July 2026, so the first delta call with an old
sincereturns all projects; subsequent calls return true deltas. - Any saved change counts. A project appears when anything on it is saved - pricing, design, customer, signature status and so on. Deletions are not reported.
- Ordering is oldest change first, so paging with
has_morenever skips a project even if changes occur mid-sync.