Example API usage (using curl)

Hi Everyone,

I’ve been looking at using Omeka S’ API to import existing websites in to our new exhibiting tool

The API docs themselves are quite light on detail and include very few examples so I’ve spent a lot of time searching for extra information to improve my understanding of the problems I’m facing.

https://omeka.org/s/docs/developer/key_concepts/api/

To try and help a future searcher (possibly me, possibly someone else) I’m posting some API usage examples. A collection of resources I used to figure things out are included but I wasn’t keeping very good records which is one reason not everything has an existing source.

Creating an item set

This should be done before creating a site so you can configure the site to use your resulting item set from the start.

This appears to have no input validation: sets can be created with empty or duplicate titles.

curl -H 'Content-type: application/json' --data-raw '{"dcterms:title" : [ { "type" : "literal", "property_label" : "Title", "@value" : "Tasmanian History Companion (Images)", "property_id" : 1 } ]}' -s 'https://example.org/api/item_sets?key_identity=EXAMPLE_KEY&key_credential=EXAMPLE_CRED' |json_pp

Creating a site

curl -H 'Content-type: application/json' --data-raw '{"o:slug": "a-new-site-2", "o:theme": “my-default", "o:title": "My testing site has a name", "o:site_item_set" : [ { "o:item_set" : { "o:id" : "896" } } ]}' -s 'https://example.org/api/sites?key_identity=EXAMPLE_KEY&key_credential=EXAMPLE_CRED' |json_pp

Creating a page in a site

This requires a title, slug and site be provided. Its also quite fussy about slugs, only allowing [09azAZ-_].

curl -H 'Content-type: application/json' --data-raw '{ "o:slug": "A-Apple-htm", "o:site": { "@id" : "https://example.org/api/sites/12", "o:id": 12 }, "o:title": "My first API test page" }' -s 'https://example.org/api/site_pages?key_identity=EXAMPLE_KEY&key_credential=EXAMPLE_CRED' |json_pp
{
   "o:block" : [
      {
         "o:data" : [],
         "o:layout" : "pageTitle",
         "o:attachment" : []
      }
   ],
   "@id" : "https://example.org/api/site_pages/119",
   "o:site" : {
      "o:id" : 12,
      "@id" : "https://example.org/api/sites/12"
   },
   "o:created" : {
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime",
      "@value" : "2019-01-29T05:05:02+00:00"
   },
   "o:slug" : "A-Apple-htm",
   "@type" : "o:SitePage",
   "o:id" : 119,
   "o:modified" : {
      "@value" : "2019-01-29T05:05:02+00:00",
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime"
   },
   "o:title" : "My first API test page",
   "@context" : "https://example.org/api-context"
}

In some cases (like this page creation) lack of authentication returns a ‘not found’ error rather than ‘denied’ which I found rather unexpected (though was able to rationalise afterward)

curl -H 'Content-type: application/json' --data-raw '{ "o:slug": "test_01", "o:site": { "@id" : "https://example.org/api/sites/12", "o:id": 12 }, "o:title": "My first API test page" }' -s https://example.orgapi/site_pages |json_pp
{
   "errors" : {
      "error" : "Omeka\\Entity\\Site entity with criteria {\"id\":12} not found"
   }
}

Creating an item

This creates an item without any media attached.
Will happily make entries with no titles or duplicate titles, but if a title is to be included all values in dcterms:title must be filled.

curl -H 'Content-type: application/json' --data-raw '{ "dcterms:title" : [ {"property_id": 1, "property_label" : "Title", "@value" : "My snappy title", "type" : "literal" } ], "@type" : "o:Item", "o:item_set" : [ {"o:id": 894}], "o:media" : [] }' -s 'https://example.org/api/items?key_identity=EXAMPLE_KEY&key_credential=EXAMPLE_CRED' |json_pp
{
   "o:item_set" : [
      {
         "@id" : "https://example.org/api/item_sets/894",
         "o:id" : 894
      }
   ],
   "@context" : "https://example.org/api-context",
   "o:is_public" : true,
   "o:resource_class" : null,
   "@id" : "https://example.org/api/items/905",
   "o:created" : {
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime",
      "@value" : "2019-01-30T00:06:24+00:00"
   },
   "dcterms:title" : [
      {
         "property_id" : 1,
         "@value" : "My snappy title",
         "property_label" : "Title",
         "type" : "literal"
      }
   ],
   "o:modified" : {
      "@value" : "2019-01-30T00:06:24+00:00",
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime"
   },
   "o:resource_template" : null,
   "o:media" : [],
   "@type" : "o:Item",
   "o-module-comment:comment" : [],
   "o:owner" : {
      "o:id" : 24,
      "@id" : "https://example.org/api/users/24"
   },
   "o:id" : 905
}

Item with media included

I had to re-read the API docs several times, plus understand the various examples using curl before I understood how it was supposed to tie together - now it seems so obvious.

curl -F 'data={ "dcterms:title" : [ {"property_id": 1, "property_label" : "Title", "@value" : "My snappy title API upload style", "type" : "literal" } ], "@type" : "o:Item", "o:item_set" : [ {"o:id": 894}], "o:media" : [{"o:ingester": "upload", "file_index": "0", "o:item": {}, "dcterms:title" : [ { "property_id" : 1, "property_label" : "Title", "@value" : "My media upload title", "type" : "literal" } ]}] }' \
>         -F 'file[0]=@./test-file.txt' -s 'https://example.org/api/items?key_identity=EXAMPLE_KEY&key_credential=EXAMPLE_CRED' |json_pp
{
   "@id" : "https://example.org/api/items/910",
   "@context" : "https://example.org/api-context",
   "o:modified" : {
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime",
      "@value" : "2019-01-30T00:57:29+00:00"
   },
   "o:media" : [],
   "o:owner" : {
      "@id" : "https://example.org/api/users/24",
      "o:id" : 24
   },
   "o:item_set" : [
      {
         "o:id" : 894,
         "@id" : "https://example.org/api/item_sets/894"
      }
   ],
   "o:resource_class" : null,
   "o:created" : {
      "@value" : "2019-01-30T00:57:29+00:00",
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime"
   },
   "dcterms:title" : [
      {
         "property_id" : 1,
         "property_label" : "Title",
         "type" : "literal",
         "@value" : "My snappy title API upload style"
      }
   ],
   "o-module-comment:comment" : [],
   "o:is_public" : true,
   "o:resource_template" : null,
   "@type" : "o:Item",
   "o:id" : 910
}

Update
Multiple files can be uploaded with the same item, you are required to add extra metadata for each file in your uploads media section or the un described files will be ignored.

curl -F 'data={ "dcterms:title" : [ {"property_id": 1, "property_label" : "Title", "@value" : "My snappy title API upload style", "type" : "literal" } ], "@type" : "o:Item", "o:item_set" : [ {"o:id": 894}], "o:media" : [{"o:ingester": "upload", "file_index": "0", "o:item": {}, "dcterms:title" : [ { "property_id" : 1, "property_label" : "Title", "@value" : "My media upload title", "type" : "literal" } ]},{"o:ingester": "upload", "file_index": "1", "o:item": {}, "dcterms:title" : [ { "property_id" : 1, "property_label" : "Title", "@value" : "My media upload title for file two", "type" : "literal" } ]}] }'  -F 'file[0]=@./test-file.txt' -F 'file[1]=@./test-file-2.txt' -s 'https://example.org/api/items?key_identity=EXAMPLE&key_credential=EXAMPLE' |json_pp
{
   "o:is_public" : true,
   "o-module-comment:comment" : [],
   "o:resource_template" : null,
   "@context" : "https://example.org/api-context",
   "o:resource_class" : null,
   "o:id" : 926,
   "o:item_set" : [
      {
         "o:id" : 894,
         "@id" : "https://example.org/api/item_sets/894"
      }
   ],
   "o:owner" : {
      "o:id" : 24,
      "@id" : "https://example.org/api/users/24"
   },
   "o:modified" : {
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime",
      "@value" : "2019-01-30T01:47:20+00:00"
   },
   "o:created" : {
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime",
      "@value" : "2019-01-30T01:47:20+00:00"
   },
   "dcterms:title" : [
      {
         "property_label" : "Title",
         "property_id" : 1,
         "type" : "literal",
         "@value" : "My snappy title API upload style"
      }
   ],
   "@id" : "https://example.org/api/items/926",
   "o:media" : [
      {
         "o:id" : 927,
         "@id" : "https://example.orgapi/media/927"
      },
      {
         "o:id" : 928,
         "@id" : "https://example.org/api/media/928"
      }
   ],
   "@type" : "o:Item"
}

Uploading files/media individually

Item ID (of the item to be attached to) has to already be known, so if possible upload media with the item it is joined with
No sanity checking is performed, this same file can be added repeatedly without warning.

curl -s -F 'file[0]=@./test-file.txt' -F 'data={"o:ingester": "upload", "file_index": "0", "o:item": {"o:id": 888}}' 'https://example.org/api/media?key_identity=EXAMPLE_KEY&key_credential=EXAMPLE_CRED' |json_pp 
{
   "o-module-alt-text:alt-text" : null,
   "o:modified" : {
      "@value" : "2019-01-29T23:46:49+00:00",
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime"
   },
   "o:ingester" : "upload",
   "@type" : "o:Media",
   "o:lang" : null,
   "o:source" : "test-file.txt",
   "@context" : "https://example.org/api-context",
   "o:id" : 900,
   "o:resource_class" : null,
   "o:thumbnail_urls" : [],
   "o:sha256" : "22a4b014c1724699531d0cb649cf8c29db5e6461a37df4f4654df520779fe2d7",
   "o:renderer" : "file",
   "o:owner" : {
      "@id" : "https://example.org/api/users/24",
      "o:id" : 24
   },
   "@id" : "https://example.org/api/media/900",
   "o:media_type" : "text/plain",
   "o:created" : {
      "@value" : "2019-01-29T23:46:49+00:00",
      "@type" : "http://www.w3.org/2001/XMLSchema#dateTime"
   },
   "o:filename" : "40e82c897fd6fe517eec697085033a4967eba765.txt",
   "o:is_public" : true,
   "data" : [],
   "o:item" : {
      "@id" : "https://example.org/api/items/888",
      "o:id" : 888
   },
   "o:original_url" : "https://example.org/files/original/40e82c897fd6fe517eec697085033a4967eba765.txt",
   "o:resource_template" : null,
   "o:size" : 24
}

Also note that extensions need to sit in the permitted list - configurable at https://example.org/admin/setting

References

Thats all I have at the moment but hopefully it helps someone else to understand the Omeka S API.

Karl.

6 Likes

The examples (the lack thereof, anyway) are an obvious issue, but could you mention other specific things you found lacking/missing/misleading about the API documentation page? That’s a pretty recent “fresh start” on that particular part of the documentation so I’m more than happy to receive feedback on its shortcomings.

Hi John,

Thanks for getting in touch.

Examples are possibly the biggest thing as having a working baseline allows tweaking and experimentation.

For reference, here are the items from my original post (slightly reworded)

  • When creating items sets title can be empty and it can be duplicated

  • Creating a site requires several fields (iirc slug, title and theme)

  • Creating a page requires several values to be provided (title, slug, site)

  • A page slug (and I’m guessing also a sites?) uses the very restrictive set of 09azAZ-_

  • “Permission denied” can be returned as “not found” , causing some confusion

    • Adding “are you authenticated?” To the not found would be a direct pointer from the error
  • Creating items will make entries with no titles or duplicate titles

    • Including duplicate content. The only thing that is different in some of my tests is the creation time!
  • Media with same filename/title/everything can be uploaded

  • Documentation doesn’t reference the permitted extensions list for media uploads.

Other things which aren’t included above:

  • A resource which describes required fields for each api call would be useful

  • Ditto one describing possible expected errors.

    • That could be a link to the source itself, if that shows those items clearly enough
  • property_id being required within dcterms:title - as noted in the linked GitHub issues - the json layout is a rather confusing situation.

    • This is alluded to in “Format” but no detail is provided
  • I was expecting slugs to be generated if not supplied though I don’t object to it being mandatory (nothing wrong with being explicit)

  • API Operations aren’t listed in a concise format anywhere. If they were directly linkable they would appear in the navigation and that would help here.

Things I never figured out were

  • How to use search properly. It would be nice but with item sets the size I’m working with - dozens up to hundreds of images - I can pull the lot and extract what I need manually
  • How to create items+media via a page creation call - if its even possible.
  • If uploading three media as part of an item, if #1 and #3 are descried in metadata and #2 is not; what is the behaviour (I haven’t tried this, I just wondered after doing my tests before)

If I think of anything specific I’ll update here again.

Karl.

1 Like

You mean here like when editing a site page, create an item/media and attach that new item all at once? That’s not possible.

Thats what I meant!

I’m ok with doing it in multiple calls - after all, it only requires two - but thought given the way items and media could be done together it may be possible to do a full mega-roll-up of everything in one go.

Karl.

Couple more I had lying around for reference

# Search for all items in item_set_id 929                                                                                                                                            
curl -s 'https://example.org/api/items?key_identity=ident&key_credential=cred&item_set_id=929'
# Query all properties avvailable; these appear to be DC terms fields or something very similar. Any of these should be usable in the json                                           
curl -s 'https://example.org/api/properties?key_identity=ident&key_credential=cred'    
[                                                                                                                                                                                    
25 item list
]