testing zotero://select

I was looking through the code and found zotero://select which seemed useful, but I noticed that item id's were not consistent across sync'd machines. That is, zotero://select/9/400 brings up a different item on different machines, even though both are sync'd otherwise. What is the expected behavior? Would a reset fix this?

My thinking was that zotero://select would be a handy way to access items from emacs (or any other external application, as suggested in http://forums.zotero.org/discussion/1286/freemind/).

I don't know if it matters, but both machines are linux and firefox 3.5 and I'm using 2.0b6.5
  • The custom protocol handler functions need to be switched to use item keys (which are consistent across machines) rather than item ids (which are no longer consistent in Zotero 2.0). We'll do this for 2.0 Final.
  • Thanks Dan.

    Is there any public timeline for 2.0 Final? I'm just wondering if it's worth investigating a workaround for myself using MozRepl.
  • In the latest dev build, zotero://select has been updated to support library-key hashes, which are consistent across machines, along with ids. A library-key hash combines a libraryID (which can be either a user library or a group library) and an object key, which, unlike an id, is guaranteed to be consistent across machines. (The hash just looks like "12345_ABCD9876".) The easiest way to find a hash via the UI (in the latest dev build) is to generate a report for an individual item. In code, you can get one with Zotero.Items.getLibraryKeyHash(item), where |item| is a Zotero.Item object, or by passing the libraryID and key from the object to Zotero.Items.makeLibraryKeyHash(libraryID, key). The current personal library is always 0.
  • Is there a way to combine zotero://select with zotero://fullscreen? (i.e., in a single url?)
  • I don't know if this deserves a new thread or not, but I have figured out how to use zotero://select in a useful way (for me). After some trial and error with MozRepl, trying to write a Firefox extension, I realized that an export translator was what I needed and might be useful for others.

    For me, the problem is workflow and being able to quickly drag Zotero items into emacs, where I work in LaTeX or org-mode, and from emacs to open Zotero to get at my items. I don't want to worry about an intermediate step of exporting from Zotero to a BibTeX file, and lose the easy access to the snapshot, pdf, etc in Zotero.

    So, I have two things: a translator for Zotero and a small elisp function (at the bottom of this post).

    The code below can be copied into a file (ZotSelect.js) and put in the translators directory (on Mac: ~/Library/Application Support/Firefox/Profiles//zotero/translators on Linux: ~/.mozilla/firefox/profiles//zotero/translators)

    Restart Firefox. Zotero should load the new translator; it should show up in the Export pane of the Preferences. Select ZotSelect as the default export.

    Now, I can drag and drop (or use the Quick Copy keyboard shortcut) from Zotero to emacs. It creates one or more links that look like this:


    The can be copied into the Firefox location bar and loaded, and Zotero pops up with the current item highlighted. The "creator_title_year" bit is created using the same code the BibTeX exporter uses to generate a citation key (I understand that it's possible for one translator to re-use code in another translator, but due to time constraints, I haven't bothered, and just copied the code.) The goal for me was something I can quickly search-and-replace when it's time to run BibTeX.

    "creator":"Scott Campbell",
    "lastUpdated":"2010-03-12 10:51:00"

    Zotero.configure("dataMode", "block");
    Zotero.addOption("exportCharset", "UTF-8");

    //%a = first author surname
    //%y = year
    //%t = first word of title
    var citeKeyFormat = "%a_%t_%y";

    var numberRe = /^[0-9]+/;
    // this is a list of words that should not appear as part of the citation key
    var citeKeyTitleBannedRe = /(\s+|\b)(a|an|from|does|how|it\'s|its|on|some|the|this|why)(\s+|\b)/g;
    var citeKeyConversionsRe = /%([a-zA-Z])/;
    var citeKeyCleanRe = /[^a-z0-9\!\$\&\*\+\-\.\/\:\;\<\>\?\[\]\^\_\`\|]+/g;

    var citeKeyConversions = {
    "a":function (flags, item) {
    if(item.creators && item.creators[0] && item.creators[0].lastName) {
    return item.creators[0].lastName.toLowerCase().replace(/ /g,"_").replace(/,/g,"");
    return "";
    "t":function (flags, item) {
    if (item["title"]) {
    return item["title"].toLowerCase().replace(citeKeyTitleBannedRe, "").split(" ")[0];
    return "";
    "y":function (flags, item) {
    if(item.date) {
    var date = Zotero.Utilities.strToDate(item.date);
    if(date.year && numberRe.test(date.year)) {
    return date.year;
    return "????";

    function buildCiteKey (item,citekeys) {
    var basekey = "";
    var counter = 0;
    citeKeyFormatRemaining = citeKeyFormat;
    while (citeKeyConversionsRe.test(citeKeyFormatRemaining)) {
    if (counter > 100) {
    Zotero.debug("Pathological BibTeX format: " + citeKeyFormat);
    var m = citeKeyFormatRemaining.match(citeKeyConversionsRe);
    if (m.index > 0) {
    //add data before the conversion match to basekey
    basekey = basekey + citeKeyFormatRemaining.substr(0, m.index);
    var flags = ""; // for now
    var f = citeKeyConversions[m[1]];
    if (typeof(f) == "function") {
    var value = f(flags, item);
    Zotero.debug("Got value " + value + " for %" + m[1]);
    //add conversion to basekey
    basekey = basekey + value;
    citeKeyFormatRemaining = citeKeyFormatRemaining.substr(m.index + m.length);
    if (citeKeyFormatRemaining.length > 0) {
    basekey = basekey + citeKeyFormatRemaining;

    // for now, remove any characters not explicitly known to be allowed;
    // we might want to allow UTF-8 citation keys in the future, depending
    // on implementation support.
    // no matter what, we want to make sure we exclude
    // " # % ' ( ) , = { } ~ and backslash

    basekey = basekey.replace(citeKeyCleanRe, "");
    var citekey = basekey;
    var i = 0;
    while(citekeys[citekey]) {
    citekey = basekey + "-" + i;
    citekeys[citekey] = true;
    return citekey;

    function doExport() {
    // Zotero.write("zotero://select//");
    // Zotero.write("\n");

    var citekeys = new Object();
    var item;
    while(item = Zotero.nextItem()) {
    var library_id = item.LibraryID ? item.LibraryID : 0;

    // create a unique citation key
    var citekey = buildCiteKey(item, citekeys);

    // write citation key


    I use the following elisp function when on the zotero://select// item in emacs:

    (defun smc/zot-select ()
    "select on current 'zotero://select//HASH' using fresno"
    (let (zot-select js l)
    (re-search-backward " \\|^")
    (if (re-search-forward "\\(zotero:\\/\\/select\\/\\/0_[[:alnum:]]\\{8\\}\\)" (line-end-position) t)
    (setq zot-select (match-string-no-properties 1))
    ;; (message zot-select)
    (shell-command (concat "fresno -p \"" zot-select "\"")))
    (message "no match")))))

    Fresno is a complement to MozRepl (http://simile.mit.edu/wiki/Fresno) I do this because Firefox on the mac is braindead. It can't open "zotero://select" urls without fresno, although it does on Linux. And this is simpler than going through MozRepl.

    I don't know if this will help anyone else, and if there are bugs or anything I make no promises or guarantees, but I'll happily accept advice or corrections. For now, it works for me. It may not work with shared libraries, or anything other than my exact circumstances.

    Certainly, it was an interesting challenge, and it makes Zotero that much more useful to me.
  • Thanks for the translator-- this is something I've been thinking about doing myself. I don't use BibTeX, so I made some simplifications (see the code on github) so that I could use it to paste links to items from within Zotero notes/reports, or from anywhere on my computer.

    The code works; dragging-and-dropping will yield:
    <a href='zotero://select//0_SBIKZJUN'>Keenan. Dec., 1967.
    <i>Muscovy and Kazan: Some Introductory Remarks on the
    Patterns of Steppe Diplomacy</i></a>

    Problems remain, however.

    First, Zotero doesn't let export translators send formatted text to the clipboard (the appropriate code for exporting via styles is at fileInterface.js:376). This means that I cannot simply drag a link into my word processor, or a Zotero note, since the HTML source will not be interpreted as HTML, but displayed as-is. I think that it would be sufficient to have translators and styles use the same *ToClipboard methods.

    Second, I can't use the Zotero.Items.getLibraryKeyHash(item) code Dan mentioned above, since it's not available to translators (it seems).

    Third, I would like to use an existing style, but just wrap its output in a link that points to the item in my library. Currently, translators don't have access to Zotero.Styles.

    Fourth, and perhaps separately from the first three issues, TinyMCE won't let me click on links, so even if I can make links, I still won't be able to use them to make navigable connections between my notes and items.

    This is an exciting development.
  • I'm glad it's useful for you, but I can't help with styles or formatted text.

    With respect to the second problem, I ran into the same issue with Zotero.Items.getLibraryKeyHash(item). That's why I copied the two lines out of chrome/content/zotero/xpcom/data/dataObjects.js. I know that's cheating but this was a quick and dirty job. The translator might break if you have shared or multiple libraries. I didn't test that.

    What I didn't mention is that this translator works if you drag the note, the attachment, or the original item: the hash refers to the individual entity, not the original item (although the citation key currently generated for notes is meaningless).

    I was surprised to read (and then test) your comment that links don't work in the notes in TinyMCE. Is this a known bug or a consequence of limiting TinyMCE for Zotero? I can see how having links in notes to other items would be very handy.

    I've often thought that it would be incredibly useful to embed zotero://select (and zotero://attachment) links in the generated reports, even if it were problematic for anyone else who read the report.
  • About TinyMCE, I believe this problem is not really a bug but because TinyMCE expects to be embedded in html content. Whereas zotero is part of the firefox "chrome". There are other things that don't work as they normally would, like firefox's built-in spell-checking.
  • @alexuw: I suppose it makes sense that TinyMCE wouldn't ordinarily support clicking on links. I think it should be feasible to tweak add an onClick action for links in the editor so that it opens links, at least when the user uses a modifier key (Ctrl-Click).

    If we can do this, then we'll be able to add working links between notes and items!

    @scottcampbell: I get a hash starting with 0_ even for items in group libraries, so I think the workaround may not work for shared items. Local is a good start, but it'd be nice for translators to receive access to more Zotero functionality...
  • Does Zotero standalone supports zotero://select? Does not seem to be working in my case.
  • I doubt it - zotero:// is a protocol that the Zotero add-on defines for Firefox, so w/o that add-in installed I don't see how it could be working - though I'm not an expert.
  • Zotero Standalone should handle the zotero:// protocol on Mac OS X and Windows. It could support the zotero:// protocol on Linux as well with the appropriate packaging.
  • edited December 5, 2015
    Is the zotero:// protocol currently supported in Standalone Zotero for Linux?

    I've read the threads listed here and in https://forums.zotero.org/discussion/24241/ and in particular I've seen the applescript solution in https://forums.zotero.org/discussion/28386/zoteroselect-for-z-standalone-select-z-item-from-external-application/ But (appart from been green with envy:-) ) I have no idea how to get something like that to work in Linux and it seems (https://github.com/zotero/zotero/issues/498#issuecomment-52443382) the answer would be no. Is that the case?
  • edited December 5, 2015
    depends on what you mean by "supported" it won't work out of the box as per that github comment, but you can register zotero:// for Zotero in Linux and then it should:
    see e.g. http://superuser.com/questions/162092/how-can-i-register-a-custom-protocol-with-xdg
  • Thanks. But I must be missing something really obvious. I created a zotero.desktop file as (adding %u to the Exec entry does not change anything)

    [Desktop Entry]
    Name=Zotero Client

    I've used both
    xdg-desktop-menu install --novendor zotero.desktop
    and adding the entry in my .local/share/applications/mimeapps.list

    but when I do something like
    xdg-open zotero://select/items/0_82UIB2W6
    xdg-open zotero://open-pdf/0_X64D5CU8/4
    I get "gvfs-open: zotero://open-pdf/0_X64D5CU8/4: error opening location: The specified location is not supported"

    (the entries are to an existing entry ---obtained using the second procedure explained in https://zoteromusings.wordpress.com/2013/04/23/zotero-item-uris-from-client/ --- and to an existing PDF, with a link freshly generated with zotfile).

    But then, I think the problem is previous and reflects I am doing something very wrong, as both

    xdg-mime query filetype zotero://select/items/0_82UIB2W6


    xdg-mime query filetype 0_82UIB2W6

    say "xdg-mime: file '0_82UIB2W6' does not exist"

    But in fact, I do not see how this would work: when I've used xdg-open and friends in the past, I associated a file type with a desktop entry that passes the file to the program I want (e.g., that is how I open PDFs by default: they are opened with Emacs because I have an entry that calls emacsclient.desktop and have associated PDFs with emacsclient.desktop). But this is not exactly the same.

    I guess I'll need to try and understand xdg better.
  • disclaimer: I haven't actually done this but I'm pretty sure that last line should be

    (note that org-protocol was used to open org-protocol://)
  • Thanks. Fixed that and now I no longer get errors and when I use it, it opens Zotero if it is not running. But it actually does nothing else so that

    xdg-open zotero://select/items/0_82UIB2W6

    will not take me to the entry, for instance. I think the problem is that what I have done is just allow me to type

    xdg-open zotero://something

    so that it is equivalent to calling


    but the arguments (e.g., things like //select/items/0_82UIB2W6 or //open-pdf/0_X64D5CU8/4) are not passed to Zotero standalone.
  • have you tried this with Zotero already running? In that case, you could also have debug run and see what Zotero is actually getting and doing with it. But it may also just work (IIRC, there have been other reports of zotero://select not working when used before Zotero is running
  • Yes, sorry, I should have been more explicit: I tried it with Zotero already running, and nothing happens, except focus moves from the shell to Zotero. But the entry is not shown or the pdf opened.

    I enabled debugging: nothing is shown in the output. But then, it makes sense that nothing is shown: it seems to me that nothing is being passed on to Zotero standalone. In other words

    xdg-open zotero://select/items/0_82UIB2W6

    is the same as

    xdg-open zotero

    (so that even something silly as

    xdg-open zotero:aeiou-some-silly-string

    does nothing but change focus and specifically it does not trigger any output in the degug console).

    I've google around, and I do not see how to pass arguments to Zotero standalone in Linux (though this is clearly possible in Macs, as per https://forums.zotero.org/discussion/28386/zoteroselect-for-z-standalone-select-z-item-from-external-application/)
  • In fact, since the zotero.desktop file is just a way to send things to the zotero process, simpler commands do not have any effect either. Variations of

    zotero zotero://select/items/0_82UIB2W6

    do nothing, except move focus to Zotero (or open it if not running). So, as I said, the problem seems to be that I do not know how to get the Zotero Standalone process, under Linux, to listen/accept arguments.

    What I am missing is a way to do the equivalent of

    tell app "Zotero" to open location "zotero://select/items/0_82UIB2W6
  • edited December 20, 2015
    willsALMANJ, the developer of Zutilo, has found out how to get this to work, as explained here:


    First, note that you should not try to run "run-zotero.sh" or however the Mozilla script might be called in your system ---the script might be under "/usr/bin" and be called "zotero" (see https://github.com/zotero/zotero-standalone-build/issues/39). You should use the zotero binary. So locate where the actual zotero binary executable lives. For instance, if you download the Zotero-4.0.28_linux-x86_64.tar.bz2 from https://www.zotero.org/download/, when you tar -jxf the file, you will get a directory "Zotero_linux-x86_64" that contains a binary executable file called "zotero", as well as a "run-zotero.sh", and a bunch of other files; you want to call the "zotero" one directly, not the "run-zotero.sh" script. In some Linux distributions at least, that binary is under "/usr/lib/zotero".

    Now, if you already have Zotero standalone running (won't work for now if Zotero is not running ---https://github.com/zotero/zotero-standalone-build/issues/40), you can just pass the URL as:

    /somewhere/Zotero_linux-x86_64/zotero --url zotero://select/items/0_someID

    Thus, the zotero.desktop file so that xdg-open works might be (modify the path to the zotero binary accordingly)

    [Desktop Entry]
    Name=Zotero Client
    Exec=/wherever/Zotero_linux-x86_64/zotero --url %u

    The above works with "zotero://select/items". It does not seem to work with "zotero://open-pdf", from Zotfile, but it is easy enough to write a script, called from zotero.desktop, that will call zotero when a "select/items" is passed, and will call your pdf viewer of choice for it to open the PDF at the correct page ---say, invoking Okular or Emacs. For instance, my zotero.desktop file is

    [Desktop Entry]
    Name=Zotero Client
    Exec=/home/ramon/bin/zotero-protocol.sh %u
    # Exec=/home/ramon/Sources/Zotero_linux-x86_64/zotero --url %u

    Where zotero-protocol.sh is (with proper indentation :-) )

    shopt -s extglob ## Remember to set extended options

    USE_EMACS=1 ## Open PDFs with Emacs (+ pdf-tools and org-pdfview)
    EMACS_NAME="emacsclient -s gral -c"

    ## For now, we only handle "zotero://open-pdf"
    ## and "zotero://select/items/some_dir".

    if [[ $(echo "$1" | grep $OPEN_PDF_HANDLE -c) -eq 1 ]]; then
    DIR=$(echo "$1" | sed 's/0_//' | cut -d / -f 4)
    PAGE=$(echo "$1" | sed 's/0_//' | cut -d / -f 5)
    ## There should be a single PDF per directory. See
    ## https://www.zotero.org/support/attaching_files search for "each
    ## file has its own subdirectory"
    if [[ USE_EMACS -eq 1 ]]; then
    $EMACS_NAME --eval "(progn (org-pdfview-open \"$FILEANDPAGE\") (delete-other-windows))" > /dev/null
    elif [[ $(echo "$1" | grep $ZOTERO_SELECT_ITEMS -c) -eq 1 ]]; then
    $ZOTERO_BINARY --url $1
    echo -e "For now, we only handle calls with $OPEN_PDF_HANDLE or $ZOTERO_SELECT_ITEMS \n"
    exit 1
  • To clarify - since I can't seem to find it anywhere - the itemkey noted above is unique across all of Zotero, correct? the hash value for a particular item, without the library ID, is unique? I'm building something that uses the API, and need to document which items are sync'd and which aren't - is this the field to use? or do I also need to store the UserID and GroupID? i.e. is the itemkey unique to the library or to all of Zotero?
  • if you're going to use the API, you need the user or group ID anyway, don't you? But for your question: the itemkey is only tested to be unique per library, but with 36^8 possible item keys, there aren't going to be many duplicates...
  • Reading the code of a 'zotselect-link' translator, I noticed that the item ID, e.g., '0_82UIB2W6' in the link 'zotero://select/items/0_82UIB2W6' consists of `library_id+'_'+item.key`. So, I'm wondering if a user could move an item from one library to another so that it would break the Zotero link. Also, for the local database, is the library_id always 0?
  • edited October 22, 2020
    Moving an item to a different library will always change its ID, so that'll definitely break the select link, yes.
    And yes, pretty MyLibrary is always 0 for the purpose of select (see dstillman to that extent towards the top of this thread). FWIW, you can now also use zotero://select/library/items/NWGAIEWW to the same effect -- that maps more elegantly to the web URIs. For groups, thats
    Where 487712 is the same library ID as used by the web API.
  • Yes, zotero://select/items/0_82UIB2W6 is an old-style URL that shouldn't be used anymore. The new format would be as adamsmith says.
    Where 487712 is the same library ID as used by the web API
    (To avoid confusion, technically this is the "group id". libraryID is a different internal thing.)
  • Thank you for the excellent suggestions: these URLs look much cleaner. From code examples I have seen, it seems that Zotero doesn't have a command line API for the local library like `zotero --url zotero://select/library/items/GetSelectedItemID` or `zotero://select/library/items/NWGAIEWW&AddAttachmentFileLink=/path/to/myfile`. Is this correct? It would make a big difference for inter-app communication and external commands.

    Regarding different identifiers of a reference among libraries, it seems interesting that DOIs could be a global identifier unifying references across libraries for citation relevant metadata -- attachments and user-generated content in the database could differ of course. I guess Zotero uses the DOI already for the identification of duplicates. Also, such a global identifier is limited to items that have a DOI. But, a DOI or a shortDOI is sufficient for doi.org to generate a properly formatted entry for a bibliography, e.g., `curl -LH "Accept: text/x-bibliography; style=apa" "https://doi.org/10.1056/nejmoa2028836"` and `curl -LH "Accept: text/x-bibliography; style=apa" "https://doi.org/ffjp"`. The style argument supports the styles of the large CSL library I looked up at the DOI Citation Formatter https://citation.crosscite.org/.

    My primary interest in these concepts is for a note-taking or Zettelkasten system and communication with other scientists. For notes or communications independent of my Zotero library, a global identifier is helpful. Locally, I use a short Zotero handler for linking to database items `zot:<<itemID>>`, e.g., `zot:NWGAIEWW`. A very small script takes the itemID, adds it to the URL template, and sends it to Zotero.
  • From code examples I have seen, it seems that Zotero doesn't have a command line API
    correct. I think it's generally on the devs list but probably not high priority.
    Regarding different identifiers of a reference among libraries, it seems interesting that DOIs could be a global identifier unifying references across libraries for citation relevant metadata
    For your purposes, yes. You could easily write a translator that uses a DOI link where it exists and a zotero://select link otherwise. But there are plenty of scenarios where a global ID won't do: if there are notes attached to your item, if you did metadata clean-up (CrossRef metadata is good, but by no means perfect), etc.
  • It would make a big difference for inter-app communication and external commands.
    Inter-app comms with Zotero is very easily added by creating an HTTP endpoint with a plugin.
Sign In or Register to comment.