I started to explore the Synology Photos API for my own project. This project is about integration of images from Synology Photos into my website directly by using API calls. The informations here are by far not complete. It's just a start. Your commits/pull requests will highly be appreciated!
There are some files in the directory /volume1/@appstore/SynologyPhotos/usr/webapi
, which are an entry point to the Synology Photos API.
- SYNO.Foto.lib. Here is a formatted/readable version of the json file.
- SYNO.FotoTeam.lib Here is a formatted/readable version of the json file.
BTW: The libraries for the Synology APIs are available in the directory
/usr/syno/synoman/webapi
of your DS.
The Synology File Station API Guide gives some basic informations, how to access the Synology API. Even though the explanations are limited to the use of the FileStation, this is a starting point, to explore the Synology Photos API.
Note: For exploring the Synology Photos API you should have installed this package of course. Otherwise the API will not be listed.
You may either use the administration port 5001
or the path /photo
to retrieve information about all available APIs. Just replace <IP_ADDRESS>
with your Synology's address:
https://<IP_ADDRESS>:5001/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=all
https://<IP_ADDRESS>/photo/webapi/entry.cgi?api=SYNO.API.Info&version=1&method=query&query=all
entry.cgi
and query.cgi
can be used synonymously. Both will get the same result.
From the response you can see, that the part about the Synology Photos API lists the same APIs as we can find in the above mentioned files SYNO.Foto.lib and SYNO.Fototeam.lib:
...
"SYNO.Foto.BackgroundTask.File":{"maxVersion":1,"minVersion":1,"path":"entry.cgi","requestFormat":"JSON"},
"SYNO.Foto.BackgroundTask.Info":{"maxVersion":1,"minVersion":1,"path":"entry.cgi","requestFormat":"JSON"},
"SYNO.Foto.Browse.Album":{"maxVersion":1,"minVersion":1,"path":"entry.cgi","requestFormat":"JSON"},
"SYNO.Foto.Browse.Category":{"maxVersion":1,"minVersion":1,"path":"entry.cgi","requestFormat":"JSON"},
...
For many API calls you need to be authenticated first. You can achieve this by the SYNO.API.Auth as follows.
Just replace <IP_ADDRESS>
, <USER>
and <PASSWORD>
with your data:
https://<IP_ADDRESS>/photo/webapi/auth.cgi?api=SYNO.API.Auth&version=3&method=login&account=<USER>&passwd=<PASSWORD>
Using the POST method with curl would work like follows:
curl -X POST --data 'api=SYNO.API.Auth&version=3&method=login&account=<USER>&passwd=<PASSWORD>' https://<IP_ADDRESS>/photo/webapi/auth.cgi
You will get the following response:
{
"data":{
"did":"3ezTHmf9ekMNjM-nF8J3qI5VzsMXlJBjRcNHotIwzKTc3ju2YGB7y9IIvOPxHTSuvvZmVOxM7u7f57w7ndtmog",
"sid":"k76NZ-C7inEyhENeX7Jfu7yB28Wk_wbWEszF2ZtHSB8dcW68WQYQbTvF0Oqo21zoQuitKmoFop-kSvJQGHG1oU"
},
"success":true
}
In the browser it works as following:
https://<IP_ADDRESS>/photo/webapi/auth.cgi?api=SYNO.API.Auth&version=3&method=logout
With curl:
curl 'http://<IP_ADDRESS>/photo/webapi/auth.cgi?api=SYNO.API.Auth&version=3&method=logout'
Response should be:
{
"success":true
}
About authenticaton also see DSM Login Web API Guide.
SYNO.Foto.*
offers access to all folders in the Personal Space
in the Photos
tab and to all Albums
in the Albums
tab.
SYNO.FotoTeam.*
offers access to all folders in the Shared Space
.
There are less APIs in SYNO.FotoTeam available. The APIs with similar names should be quite the same refering to their methods and responses.
There are three levels of authentication for the APIs, see the authLevel
in the files SYNO.Foto.lib and SYNO.Fototeam.lib.
authLevel
can get the values 0, 1, 2.
Value | Description |
---|---|
0 | No authentication needed. For display of general informations. |
1 | Authentication needed |
2 | No authentication needed. If authenticated accessible area might be different. |
API name | Description |
---|---|
SYNO.Foto.BackgroundTask.File | |
SYNO.Foto.BackgroundTask.Info | |
SYNO.Foto.Browse.Album | Handle albums. Normal and condition(al) albums are listed. |
SYNO.Foto.Browse.Category | |
SYNO.Foto.Browse.ConditionAlbum | Handle only condition(al) albums. |
SYNO.Foto.Browse.Diff | |
SYNO.Foto.Browse.Folder | Handle only folders in the Personal Space . |
SYNO.Foto.Browse.GeneralTag | |
SYNO.Foto.Browse.Geocoding | |
SYNO.Foto.Browse.Item | Handle an item in the Personal Space . Example methods include add_tag , copy , delete , get , get_exif , get_tag , list , move , rename , set |
SYNO.Foto.Browse.NormalAlbum | Handle only normal albums. |
SYNO.Foto.Browse.Person | |
SYNO.Foto.Browse.RecentlyAdded | |
SYNO.Foto.Browse.Timeline | |
SYNO.Foto.Browse.Unit | |
SYNO.Foto.Download | Retrieve/download the original file. |
SYNO.Foto.Favorite | |
SYNO.Foto.Index | |
SYNO.Foto.Migration | |
SYNO.Foto.PublicSharing | Get all text strings for user interface (UI). |
SYNO.Foto.Search.Filter | List all available search filters. |
SYNO.Foto.Search.Search | Search album and items. |
SYNO.Foto.Setting.Admin | |
SYNO.Foto.Setting.Guest | Get settings for guests. |
SYNO.Foto.Setting.MobileCompatibility | |
SYNO.Foto.Setting.TeamSpace | |
SYNO.Foto.Setting.User | |
SYNO.Foto.Setting.Wizard | |
SYNO.Foto.Sharing.Misc | |
SYNO.Foto.Sharing.Passphrase | |
SYNO.Foto.Streaming | |
SYNO.Foto.Thumbnail | Get thumbnail(s). |
SYNO.Foto.Upload.Item | |
SYNO.Foto.UserInfo |
API name | Description |
---|---|
SYNO.FotoTeam.BackgroundTask.File | |
SYNO.FotoTeam.Browse.Diff | |
SYNO.FotoTeam.Browse.Folder | Handle only folders in the Shared Space . |
SYNO.FotoTeam.Browse.GeneralTag | |
SYNO.FotoTeam.Browse.Geocoding | |
SYNO.FotoTeam.Browse.Item | Handle an item in the Shared Space . Example methods include add_tag , copy , delete , get , get_exif , get_tag , list , move , rename , set |
SYNO.FotoTeam.Browse.Person | |
SYNO.FotoTeam.Browse.RecentlyAdded | |
SYNO.FotoTeam.Browse.Timeline | |
SYNO.FotoTeam.Browse.Unit | |
SYNO.FotoTeam.Download | Retrieve/download the original file |
SYNO.FotoTeam.Favorite | |
SYNO.FotoTeam.Index | |
SYNO.FotoTeam.Search.Filter | List all available search filters. |
SYNO.FotoTeam.Search.Search | Search album and items. |
SYNO.FotoTeam.Sharing.Passphrase | |
SYNO.FotoTeam.Streaming | |
SYNO.FotoTeam.Thumbnail | Get thumbnail(s). |
SYNO.FotoTeam.Upload.Item |
In the directories API/SYNO.Foto/ and API/SYNO.FotoTeam/ the descriptions for the APIs will be listed for every method. The files name pattern is <name of API>.<name of method>.md
, i.e. SYNO.Foto.Browse.Album.list.md
(API: SYNO.Foto.Browse.Album, method: list).
I only checked a few APIs, which I want to use for my application and I didn't check any available parameter for request and response.
Please let me know, if you want to participate in this documentation. I will add you to the contributors then.
The following call gets all folders in the directory /volume1/photo
from the DS. If you are logged in, you will get a list of all folders, to which you are granted access. If you are not logged in, you will get a lists of all public shared folders.
https://<IP_ADDRESS>/photo/webapi/entry.cgi?api=SYNO.FotoTeam.Browse.Folder&version=1&method=list_parents
Response might look like following:
{
"data":{
"list":[
{
"id":1,
"name":"/",
"owner_user_id":0,
"parent":1,
"passphrase":"Bo00JIHnO",
"shared":true,
"sort_by":"default",
"sort_direction":"default"
},
{
"id":153,
"name":"/Mongolia",
"owner_user_id":0,
"parent":1,
"passphrase":"CFGTXzTL1",
"shared":true,
"sort_by":"default",
"sort_direction":"default"
},
]
},
"success":true
}
To get a list of subfolders from a specific folder you have to use the method list
instead of list_parents
and add the id
of a folder:
https://<IP_ADDRESS>/photo/webapi/entry.cgi?api=SYNO.FotoTeam.Browse.Folder&version=1&method=list&offset=0&limit=100&id=153
Response:
{
"data":{
"list":[
{
"id":608,
"name":"/Mongolia/20170730",
"owner_user_id":0,
"parent":153,
"passphrase":"2C9P3NUtC",
"shared":true,
"sort_by":"default",
"sort_direction":"default"
},
# ...
{
"id":776,
"name":"/Mongolia/20170805",
"owner_user_id":0,
"parent":153,
"passphrase":"RTxSv1YXb",
"shared":true,
"sort_by":"default",
"sort_direction":"default"
},
]
},
"success":true
}
Now you can list all items in this folder as following:
https://<IP_ADDRESS>/photo/webapi/entry.cgi?api=SYNO.FotoTeam.Browse.Item&version=1&method=list&offset=0&limit=100&folder_id=608&additional=["thumbnail"]
Parameter additional=["thumbnail"]
is added to get the value cache_key
for every item. cache_key
is needed for getting a specific item later.
Response:
{
"data":{
"list":[
{
"additional":{
"thumbnail":{
"cache_key":"40808_1633659236",
"m":"ready",
"preview":"broken",
"sm":"ready",
"unit_id":40808,
"xl":"ready"
}
},
"filename":"IMG_20170730_205341_1.JPG",
"filesize":1470363,
"folder_id":608,
"id":40808,
"indexed_time":1633631344618,
"owner_user_id":0,
"time":1501448021,
"type":"photo"
},
# ...
{
"additional":{
"thumbnail":{
"cache_key":"40798_1633659232",
"m":"ready",
"preview":"broken",
"sm":"ready",
"unit_id":40798,
"xl":"ready"
}
},
"filename":"IMG_20170730_074551.JPG",
"filesize":1867614,
"folder_id":608,
"id":40798,
"indexed_time":1633631344380,
"owner_user_id":0,
"time":1501400751,
"type":"photo"
}
]
},
"success":true
}
Following call will get the image into your browser:
https://<IP_ADDRESS>/photo/mo/sharing/webapi/entry.cgi?api=SYNO.FotoTeam.Thumbnail&method=get&version=1&id=40808&cache_key=40808_1633659236&type=unit&size=sm
To access shared albums was a problem, I'm quite struggling with. I want to access the thumbnails of photos in a shared album for loading them into my own web application.
https://<IP_ADDRESS>/photo/mo/sharing/EkPUCJLDI
This displays a shared album without having to be logged in (as long there's not a password set for this shared album).
These are the steps, how to get access on each file in a shared album:
We need to obtain a sharing_sid
cookie value for subsequent requests. -c cookie
stores the cookie data into the file cookie
.
curl -c cookie https://<IP_ADDRESS>/photo/webapi/entry.cgi -d 'api=SYNO.Core.Sharing.Login&method=login&version=1&sharing_id=EkPUCJLDI
Now we can list the items of this album with the following curl command:
curl -b cookie -H "x-syno-sharing: EkPUCJLDI" -d 'additional=["thumbnail","resolution","orientation","video_convert","video_meta","provider_user_id"]&offset=0&limit=4&sort_by="takentime"&sort_direction="asc"&passphrase="EkPUCJLDI"&api=SYNO.Foto.Browse.Item&method=list&version=1' https://<IP_ADDRESS>/photo/mo/sharing/webapi/entry.cgi
or directly in the browser:
https://<IP_ADDRESS>/photo/mo/sharing/webapi/entry.cgi?additional=["thumbnail","resolution","orientation","video_convert","video_meta","provider_user_id"]&offset=0&limit=4&sort_by="takentime"&sort_direction="asc"&passphrase="EkPUCJLDI"&api=SYNO.Foto.Browse.Item&method=list&version=1
With -b cookie
the content of the file cookie
will be sent.
Besides of the cookie, we also need to add the x-syno-sharing: EkPUCJLDI
to the header.
Of course the cookie could be given to the header this way too:
curl -H "cookie: sharing_sid=Kr5KTYIJn11M0c-h0UN3366oIaE5l-BX" -H "x-syno-sharing: EkPUCJLDI" -d 'additional=["thumbnail","resolution","orientation","video_convert","video_meta","provider_user_id"]&offset=0&limit=4&sort_by="takentime"&sort_direction="asc"&passphrase="EkPUCJLDI"&api=SYNO.Foto.Browse.Item&method=list&version=1' https://<IP_ADDRESS>/photo/mo/sharing/webapi/entry.cgi
The response might look as follows:
{
"data":{
"list":[
{
"additional":{
"orientation":6,
"orientation_original":6,
"provider_user_id":2,
"resolution":{
"height":4032,
"width":3024
},
"thumbnail":{
"cache_key":"25758_1633653387",
"m":"ready",
"preview":"broken",
"sm":"ready",
"unit_id":25758,
"xl":"ready"
}
},
"filename":"IMG_20210911_190642.JPG",
"filesize":3098406,
"folder_id":1438,
"id":25758,
"indexed_time":1633630824030,
"live_type":"photo",
"owner_user_id":0,
"time":1631387203,
"type":"live"
},
{
"additional":{
"orientation":1,
"orientation_original":1,
"provider_user_id":2,
"resolution":{
"height":3024,
"width":4032
},
"thumbnail":{
"cache_key":"25752_1633653385",
"m":"ready",
"preview":"broken",
"sm":"ready",
"unit_id":25752,
"xl":"ready"
}
},
"filename":"IMG_20210912_144022.JPG",
"filesize":3006194,
"folder_id":1437,
"id":25752,
"indexed_time":1633630823784,
"live_type":"photo",
"owner_user_id":0,
"time":1631457623,
"type":"live"
},
{
"additional":{
"orientation":1,
"orientation_original":1,
"provider_user_id":2,
"resolution":{
"height":1536,
"width":2048
},
"thumbnail":{
"cache_key":"25757_1633653387",
"m":"ready",
"preview":"broken",
"sm":"ready",
"unit_id":25757,
"xl":"ready"
}
},
"filename":"IMG_20210911_182919.jpg",
"filesize":188232,
"folder_id":1438,
"id":25757,
"indexed_time":1633630823000,
"owner_user_id":0,
"time":1631815585,
"type":"photo"
}
]
},
"success":true
}
Now you can get the thumbnail of each image:
curl -k -b cookie 'https://<IP_ADDRESS>/photo/mo/sharing/webapi/entry.cgi?id=25752&cache_key="25752_1633653385"&type="unit"&size="sm"&passphrase="EkPUCJLDI"&api="SYNO.Foto.Thumbnail"&method="get"&version=1&_sharing_id="EkPUCJLDI"'
As you see, the cookie is still needed.
The parameter passphrase="EkPUCJLDI"
is not needed.
Getting a thumbnail this way with curl on the html server for the website doesn't help. The thumbnail can't be used as a reference to the image in html because the cookie we get on the webserver with curl limits it's use to the server and not to the browser of someone calling the website. Therefore another solution has to be found.
When fetching photo data from the API, each photo object includes an additional
object which contains a thumbnail
object. This thumbnail
object includes a cache_key
field. The cache_key
is a unique identifier for the photo's thumbnail image.
When generating a URL to access the thumbnail image, you can include the cache_key
as a parameter along with the sid
in the URL. This allows you to bypass the session-based restrictions that would normally prevent the thumbnail image from being accessed outside of the original session in which it was requested.
Here's an example of how to fetch photo data and generate a thumbnail URL:
async function fetchPhotos(sid) {
const photosResponse = await fetch(
`https://${ip}/photo/webapi/entry.cgi?api=SYNO.Foto.Browse.Item&version=1&method=list&type=photo&offset=0&limit=5000&_sid=${sid}&additional=["thumbnail"]`
)
const photosData = await photosResponse.json()
return photosData.data.list[0] //just return the first photo as an example
}
That will return an object that looks like this:
{
id: 7713,
filename: 'IMG_20230625_095440.jpg',
filesize: 7231588,
time: 1687686880,
indexed_time: 1687683302513,
owner_user_id: 1,
folder_id: 242,
type: 'photo',
additional: {
thumbnail: {
m: 'ready',
xl: 'ready',
preview: 'broken',
sm: 'ready',
cache_key: '7713_1687701308',
unit_id: 7713
}
}
}
The cache_key
can then used as a parameter to access the web url of the photo
function getThumbnailUrl(ip, sid, photo) {
const {
id,
additional: {
thumbnail: { cache_key }
}
} = photo
return `https://${ip}/webapi/entry.cgi?api=SYNO.Foto.Thumbnail&version=1&method=get&mode=download&id=${id}&type=unit&size=xl&cache_key=${cache_key}&_sid=${sid}`
}
Note that the cache_key
is specific to each photo and its corresponding thumbnail image. It cannot be used to access other photos or their thumbnails.
It seems that this approach allows you to circumvent the issue of needing a cookie by using the cache_key
and sid
for authentication. By including them as params, you're able to access the thumbnails directly, even in a new, cookie-less session/incognito window.
With help of the Synology Photos API one can only get informations about the size of the original photo, not about the size of the thumbnails. You can only get general information about the thumbnails: availability and a general size. Sizes can have the values sm
, m
and xl
.
As far as I checked the thumbnail sizes, they are calculated as follows:
size | width | height | calculation |
---|---|---|---|
original | 2448 | 3264 | --- |
sm | 240 | 320 | 3264/2448*240=319,99999999 |
m | 320 | 427 | 3264/2448*320=426,66666666 |
xl | 1280 | 1707 | 3264/2448*1280=1706,6666666 |
Values for this file:
https://<IP_ADDRESS>/photo/mo/sharing/webapi/entry.cgi?api=SYNO.FotoTeam.Thumbnail&method=get&version=1&id=40808&cache_key=40808_1633659236&type=unit&size=<THUMBNAIL-SIZE>
The file in original resolution can be retrieved as following:
https://<IP_ADDRESS>/photo/webapi/entry.cgi?cache_key="40808_1633659236"&unit_id=[40808]&api="SYNO.FotoTeam.Download"&method="download"&version=1
size | width | height | calculation |
---|---|---|---|
original | 6000 | 4000 | --- |
sm | 360 | 240 | 6000/4000*240=360 |
m | 480 | 320 | 6000/4000*320=480 |
xl | 1920 | 1280 | 6000/4000*1280=1920 |
Values for this file (this file is in the personal space. One has to be logged in before as explained above. Or when logged in from SynologyPhotos one needs to add the valid SynoToken!):
https://<IP_ADDRESS>/photo/mo/sharing/webapi/entry.cgi?api=SYNO.Foto.Thumbnail&method=get&version=1&id=80719&cache_key=80719_1633863542&type=unit&size=<THUMBNAIL-SIZE>&SynoToken=pSAYNF9UTBZQA
The file in original resolution can be retrieved (this file is in the personal space. One has to be logged in before as explained above. Or when logged in from SynologyPhotos one needs to add the valid SynoToken!) as following:
https://<IP_ADDRESS>/photo/webapi/entry.cgi?cache_key="80719_1633863542"&unit_id=[80719]&api=SYNO.Foto.Download&method=download&version=1&SynoToken=pSAYNF9UTBZQA
The thumbnails are calculated on the lower value of both sides for every size. The value for each size is:
size | value |
---|---|
sm | 240 |
m | 320 |
xl | 1280 |
I didn't systematically check yet, what will happen, if the original size is smaller than a thumbnail minimum size. Most probably the thumbnail will not be generated in this case.
So size calculation can be done as following:
- get srcHeight (from additional.resolution.height) and srcWidth (from additional.resolution.width) of original file
- if srcHeight > scrWidth then
- xSM/xM/xXL = 240/320/1280
- srcWidth DIV srcHeight * 240/320/1280 = ySM/yM/yXL
- else
- ySM/yM/yXL = 240/320/1280
- csrcHeight DIV srcWidth * 240/320/1280 = xSM/xM/xXL