Python API Client for Close

Overview

Close API

PyPI version CircleCI

A convenient Python wrapper for the Close API.

Installation

pip install closeio

Sample Usage (of API client)

from closeio_api import Client

api = Client('YOUR_API_KEY')

# post a lead
lead = api.post('lead', data={'name': 'New Lead'})

# get 5 most recently updated opportunities
opportunities = api.get('opportunity', params={'_order_by': '-date_updated', '_limit': 5})

# fetch multiple leads (using search syntax)
lead_results = api.get('lead', params={
    '_limit': 10,
    '_fields': 'id,display_name,status_label',
    'query': 'custom.my_custom_field:"some_value" status:"Potential" sort:updated'
})

Example scripts

Check out https://github.com/closeio/closeio-api-scripts for helpful scripts already written to accomplish some common tasks.

Other Languages

There are unofficial API clients available in other languages too, thanks to some awesome contributors:

Comments
  • bulk_update_lead_info for adding a contact doesn't appear to work

    bulk_update_lead_info for adding a contact doesn't appear to work

    (closeio)--- Projects/closeio-api ‹master» python scripts/bulk_update_leads_info.py ~/Downloads/test.csv --api-key <redacted> --confirmed
    [2015-05-15 18:59:23,018] INFO Starting new HTTPS connection (1): app.close.io
    [2015-05-15 18:59:23,539] INFO line 2 updated: lead_iyTYlkzfVWKn0rH0x1yGrPX2vMOzzCsOdBK205WrzLk Plumbing Medic
    [2015-05-15 18:59:23,539] INFO summary: updated[1], new[0], skipped[0]
    (closeio)--- Projects/closeio-api ‹master» cat ~/Downloads/test.csv
    lead_id,contact_name,contact_phone,,,,,,
    lead_iyTYlkzfVWKn0rH0x1yGrPX2vMOzzCsOdBK205WrzLk,Conference Call Line,+1818-452-3980,,,,,,
    

    No contact added to https://app.close.io/lead/lead_iyTYlkzfVWKn0rH0x1yGrPX2vMOzzCsOdBK205WrzLk/

    bug 
    opened by anemitz 10
  • Clean up the code, add docstrings, and get rid of the poorly implemented async client

    Clean up the code, add docstrings, and get rid of the poorly implemented async client

    Right now the async client makes the code more complex and doesn't really add much value. I doubt it's used in the wild and if it is, then the usage is probably quite confusing and hacky. I think we might as well get rid of it.

    Examples of current confusing behavor:

    • api.get/post/put/delete is still synchronous
    • api.map expects a list of grequests.AsyncRequest, but there's no easy way to construct them.

    This PR introduces breaking changes and should be published along with a major version bump.

    TODO:

    • [x] Add mocked unit tests
    • [x] Bump version to v1.0
    • [x] Add a GitHub Release and publish on PyPI
    opened by wojcikstefan 8
  • Issue 59 - fix user reassign

    Issue 59 - fix user reassign

    Fixes #59

    All the changes are supported by TDD with functional tests hitting the live API.

    I'm not committing the tests but I can share them somewhere in case you want to have a look.

    /cc @thomasst

    opened by flevour 7
  • datetimes do not take timezone offset into account properly.

    datetimes do not take timezone offset into account properly.

    As an example, today I ran this request:

    resp = api.get('report/activity/ORGID', params=
    {'user_id':'USERID', 'date_start':'2018-01-02', 'date_end':'2018-01-02'})
    

    The response I got back was:

    {
        "revenue_created_annual_created": 0, 
        "opportunities_created": 0, 
        "revenue_lost_annual_created": 0, 
        "sms_sent": 0, 
        "leads_created": 0, 
        "opportunities_lost": 0, 
        "revenue_won_monthly": 0, 
        "revenue_won_annual": 0, 
        "leads_contacted": 3, 
        "revenue_won_one_time": 0, 
        "revenue_lost_annual": 0, 
        "revenue_lost_one_time_created": 0, 
        "revenue_lost_one_time": 0, 
        "revenue_created_monthly": 0, 
        "revenue_won_annual_created": 0, 
        "opportunities_won": 0, 
        "opportunities_created_created": 0, 
        "emails_sent": 0, 
        "revenue_created_monthly_created": 0, 
        "emails_received": 0, 
        "calls_duration_total": 0, 
        "sms_received": 0, 
        "calls_duration_average": 0, 
        "revenue_won_one_time_created": 0, 
        "revenue_created_one_time": 0, 
        "revenue_won_monthly_created": 0, 
        "revenue_lost_monthly_created": 0, 
        "opportunities_won_created": 0, 
        "opportunities_lost_created": 0, 
        "revenue_lost_monthly": 0, 
        "_queries": {
            "leads_contacted": "call(duration > 0  date >= 2018-01-01 date <= 2018-01-01) or email(direction:sent  date >= 2018-01-01 date <= 2018-01-01) or sms(direction:sent  date >= 2018-01-01 date <= 2018-01-01)", 
            "emails_received": "email(direction:received  date >= 2018-01-01 date <= 2018-01-01)", 
            "calls": "call( date >= 2018-01-01 date <= 2018-01-01)", 
            "opportunities_created": "opportunity( created >= 2018-01-01 created <= 2018-01-01)", 
            "opportunities_won_created": "opportunity(status_type:won  closed >= 2018-01-01 closed <= 2018-01-01)", 
            "opportunities_lost_created": "opportunity(status_type:lost  lost >= 2018-01-01 lost <= 2018-01-01)", 
            "sms_sent": "sms(direction:sent  date >= 2018-01-01 date <= 2018-01-01)", 
            "leads_created": " created >= 2018-01-01 created <= 2018-01-01", 
            "opportunities_won": "opportunity(status_type:won  closed >= 2018-01-01 closed <= 2018-01-01)", 
            "opportunities_created_created": "opportunity( created >= 2018-01-01 created <= 2018-01-01)", 
            "emails_sent": "email(direction:sent  date >= 2018-01-01 date <= 2018-01-01)", 
            "sms_received": "smsdate >= 2018-01-01 date <= 2018-01-01)", 
            "opportunities_lost": "opportunity(status_type:lost" lost >= 2018-01-01 lost <= 2018-01-01)"
        }, 
        "revenue_created_one_time_created": 0, 
        "calls": 0, 
        "revenue_created_annual": 0
    }
    
    

    since the bounding dates are 2018-01-01 and 2018-01-01, it means that we aren't correctly factoring in timezone when passing through date params.

    We need to add the tz.offset*-1 to the datetime in both directions for it to appear the same as it does in app.

    opened by eengoron 5
  • user_reassign script skips over objects

    user_reassign script skips over objects

    The user_reassign script doesn't update all the objects. It paginates through objects that match certain criteria (i.e. the old user), reassigns them to the new user, and then increases the offset to fetch the next batch. The problem is that when we reassign old objects, the results don't contain them anymore, and we end up skipping objects. We instead need to keep the offset at 0 and loop until no more objects are left (except for in the dry run).

    bug 
    opened by thomasst 5
  • Script to transfer leads (and nested contacts, notes, calls, etc.) from one organization to another

    Script to transfer leads (and nested contacts, notes, calls, etc.) from one organization to another

    Should take a search query as input and 2 API keys (from different orgs)

    Transfer all the leads matching the search query

    As well as all the nested objects on the leads (Contacts, Tasks, Opportunities), and the Calls & Note Activities

    Emails don't need to transfer since we'll sync them ourselves but all other lead data should transfer over.

    If lead statuses or opportunity statuses don't exist it should clearly print out that you need to set up those statuses within the destination organization first.

    enhancement 
    opened by anemitz 5
  • user reassign scripts doesn't appear to actually work

    user reassign scripts doesn't appear to actually work

    (closeio-api)--- lib/closeio-api ‹master» ./scripts/user_reassign.py --to-user-id user_wWDo6ETmCC9mSL3YQt0wdierIIKfZnR0LShSiVxEXBF --from-user-id user_fLuLxKSVFjJTePCe660YI2f2b5JpwoBjWQaPqWUjm5V -k <redacted> --all-tasks --all-opportunities -c
    

    Running the above command seems to modify the tasks and opportunities but if you run it again the see the same output / nothing actually changes.

    bug 
    opened by anemitz 4
  • Script to bulk update the country code of all addresses in leads matching a certain search query

    Script to bulk update the country code of all addresses in leads matching a certain search query

    bulk_update_address_countries.py

    inputs:

    • api key
    • old country code
    • new country code
    • leads search query (defaults to * sort:created if none) -- can be coded in script since it's hard to pass complicated queries (e.g. quotes) to cli args

    python cli script that takes these items as inputs. it should loop through each lead in the given search query, and if there are any addresses on that lead with the old country code then it should update the address with the new country code

    Would also like a read-only preview mode of what will happen and then a --confirmed flag

    @congocongo Can you work on this one after the other script?

    opened by philfreo 4
  • Add custom User Agent to every request, and expose the version in a readable way

    Add custom User Agent to every request, and expose the version in a readable way

    Closes #80

    This PR:

    • Changes the user agent sent on requests from python-requests/X to python closeio vY python-requests/X, where Y is the version of the closeio-api wrapper and X is the python-requests version
    • Adds a __version__ variable to __init__.py to keep track of the version.
    • Adds a helper to setup.py to read the __version__ variable from __init__.py to keep things consistent when installing the package.
    • Updates the version from 1.1 to 1.2

    I used the same structure as the Cleancat repo to do this.

    opened by eengoron 3
  • add pkg details to user agent

    add pkg details to user agent

    In response to https://github.com/closeio/closeio-api/issues/80.

    This modifies the default User-Agent to include package details: python closeio v{VERSION} {DEFAULT}

    Also breaks out the version constant into its' own version file. This helps to reduce the change of inconsistent versions, while also making it easy to add the seudo-standard __version__ attribute :)

    Testing

    Setup a simple server script to dump headers when making calls with the client. Then loaded the modified closeio_api and the output of the dump confirms this is working.

    ----- Request Start ----->
    
    /lead/
    Host: localhost:8000
    Connection: keep-alive
    Accept-Encoding: gzip, deflate
    Accept: */*
    User-Agent: python closeio v0.5 python-requests/2.11.1
    Content-Type: application/json
    X-TZ-Offset: -7
    Content-Length: 20
    Authorization: Basic abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx
    
    {"name": "New Lead"}
    <----- Request End -----
    
    127.0.0.1 - - [13/Mar/2017 17:25:57] "POST /lead/ HTTP/1.1" 200 -
    
    opened by JohnLZeller 3
  • Make the async version of the client easier to use

    Make the async version of the client easier to use

    This is an alternative to #78 fixing some of the async client's flaws instead of removing it altogether.

    Example usage:

    In [1]: from closeio_api import Client
    
    In [2]: api = Client('api_key', development=True, async=True)
    
    In [3]: results = api.map([
       ...:   api.post('lead', {'name': 'New Lead 1'}),
       ...:   api.post('lead', {'name': 'New Lead 2'}),
       ...:   api.post('lead', {'name': 'New Lead 3'}),
       ...:   api.post('lead', {'name': 'New Lead 4'})
       ...: ])
    
    In [4]: len(results)
    Out[4]: 4
    
    In [5]: results[0]['id']
    Out[5]: u'lead_RpzCIwspNlTUowYUvIUaNE1tp3ncVFk8x3Uw4sv96FO'
    

    I'm still conflicted whether such a deep integration of concurrency is good for this client library and its users or not. On the one hand, it makes concurrent requests easier to construct and send, and it handles API errors and retry logic properly (-ish). On the other hand, it makes the code more complex (and thus harder to read/understand), it forces a particular implementation of concurrency (green threads & gevent), and there are still some quirks with it (e.g. using the "debug" flag differs unintuitively between the sync and async client).

    @closeio/engineering what do you think?

    This PR introduces breaking changes and should be published along with a major version bump.

    opened by wojcikstefan 3
  • Update ratelimit behavior

    Update ratelimit behavior

    We want to update the behavior of our api and client to follow the draft RFC related to communicating rate limiting.

    See https://github.com/closeio/closeio/issues/28735

    opened by lmickh 0
  • APIError message should contain the response's status code

    APIError message should contain the response's status code

    So that it's obvious at a glance what kind of an error we experienced. Right now it's possible to get an enigmatic traceback:

    In [3]: api.get('me')
    Traceback (most recent call last)
    <ipython-input-3-842652bd220e> in <module>()
    ----> 1 api.get('me')
    
    /Users/wojcikstefan/Repos/closeio-api/closeio_api/__init__.py in get(self, endpoint, params, **kwargs)
        132         """
        133         kwargs.update({'params': params})
    --> 134         return self._dispatch('get', endpoint+'/', **kwargs)
        135
        136     def post(self, endpoint, data, **kwargs):
    
    /Users/wojcikstefan/Repos/closeio-api/closeio_api/__init__.py in _dispatch(self, method_name, endpoint, api_key, data, debug, **kwargs)
        108             raise ValidationError(response)
        109         else:
    --> 110             raise APIError(response)
        111
        112     def _get_rate_limit_sleep_time(self, response):
    
    APIError:
    
    opened by wojcikstefan 5
  • APIError is not very informative

    APIError is not very informative

    When a request fails because of an aborted connection or other unhandled exception the resulting APIError and the stack trace generated with it is not very informative. We should make the string representation of APIError include more detail.

    opened by lucasvo 1
Releases(v2.0)
  • v2.0(Apr 6, 2022)

    Changes in this release:

    • update 429 handling to use response headers rather than the json body
    • (Breaking Change) Drop python 2 support
    • Other minor or project-internal changes (update pytest, test on more recent python 3 versions, etc)
    Source code(tar.gz)
    Source code(zip)
  • v1.4(Dec 30, 2020)

  • 1.3(Jun 10, 2020)

  • v1.1(Feb 22, 2019)

  • v1.0(May 8, 2017)

    We're happy to publish a stable v1.0 of our Close.io API wrapper! The main highlights of this release are:

    • Significantly improved code quality, clarity, and documentation.
    • Automatic retrying of rate-limited requests.
    • Breaking change: Removal of the asynchronous code. If you relied on our async=True flag in the past, you'll have to refactor your application. You can choose to run multiple concurrent api.get/post/put/delete requests via threading, multiprocessing, concurrent.futures, gevent, etc., depending on your specific use case and environment.
    Source code(tar.gz)
    Source code(zip)
  • v0.5(Jan 24, 2017)

    • Fixed adding a trailing slash for GET requests.
    • Several fixes to the lead merge script.
    • Dropped flawed Python v3.x support.
    • Added script deleting tasks for inactive users.
    Source code(tar.gz)
    Source code(zip)
Owner
Close
The inside sales CRM of choice for SMBs. Join our eng team: http://jobs.close.com/
Close
Projeto com o objetivo de aprender o funcionamento de Consumo de APIs.

Consumindo API SuperHero Projeto com o objetivo de aprender o funcionamento de Consumo de APIs.

Deivisson Henrique 1 Dec 30, 2021
Instadev - Crack Instagram IqbalDev

Crack Instagram IqbalDev ⇨ Install Script Di Termux $ pkg update && upgrade $

Dicky Wahyudi 1 Feb 27, 2022
A secure and customizable bot for controlling cross-server announcements and interactions within Discord

DiscordBot A secure and customizable bot for controlling cross-server announcements and interactions within Discord. Within the code of the bot, you c

Jacob Dorfmeister 1 Jan 22, 2022
The successor of GeoSnipe, a pythonic Minecraft username sniper based on AsyncIO.

OneSnipe The successor of GeoSnipe, a pythonic Minecraft username sniper based on AsyncIO. Documentation View Documentation Features • Mojang & Micros

1 Jan 14, 2022
A drop-in vanilla discord.py cog to add slash command support with little to no code modifications

discord.py /slash cog A drop-in vanilla discord.py cog that acts as a translation layer to add slash command support with little to no code modificati

marshall 3 Jun 01, 2022
An interactive App to play with Spotify data, both from the Spotify Web API and from CSV datasets.

An interactive App to play with Spotify data, both from the Spotify Web API and from CSV datasets.

Caio Lang 3 Jan 24, 2022
E0 AI Bot is based on the message, it prints the answer with the highest probability using probability from the database.

E0 AI Chat Bot Based on the message, it prints the answer with the highest probability using probability from the database. Install on linux (Arch,Deb

Error 27 Dec 03, 2022
A discord bot for tracking Iranian Minecraft servers and showing the statistics of them

A discord bot for tracking Iranian Minecraft servers and showing the statistics of them

MCTracker 20 Dec 30, 2022
Telegram bot to provide links of different types of files you send

File To Link Bot - IDN-C-X Telegram bot to provide links of different types of files you send. WHAT CAN THIS BOT DO Is it a nuisance to send huge file

IDNCoderX 3 Oct 26, 2021
A Python library wrapping the iFixit (Dozuki) API.

A Python library wrapping the iFixit API. Status Working, but incomplete. Fully tested and documented. Hacking I highly recommend using virtualenv: [$

James Pearson Hughes 13 May 24, 2021
Kodi script for proper Australian weather data

Kodi Oz Weather weather.ozweather Script for Kodi for high quality Australian weather data sourced directly from the BOM. Available from the Kodi offi

Jeremy Daalder 5 Nov 24, 2022
Terraform Cloud CLI for Managing Workspace Terraform Versions

Terraform Cloud Version Manager This tiny script makes it easy to update the Terraform Version on all of the Workspaces inside Terraform Cloud. It wil

Robert Hafner 1 Jan 07, 2022
Example notebooks for working with SageMaker Studio Lab. Sign up for an account at the link below!

SageMaker Studio Lab Sample Notebooks Available today in public preview. If you are looking for a no-cost compute environment to run Jupyter notebooks

Amazon Web Services 304 Jan 01, 2023
wyscoutapi is an extremely basic API client for the Wyscout API (v2 & v3) for Python

wyscoutapi wyscoutapi is an extremely basic API client for the Wyscout API (v2 & v3). Usage Install with pip install wyscoutapi. To connect to the Wys

Ben Torvaney 11 Nov 22, 2022
A better rename and convert bot with upload mode option and Auto detection

A better rename and convert bot with upload mode option and Auto detection

Code X Mania 2 Nov 09, 2021
Web-music-bot - A telegram bot which get a *site Url* and sends all songs contain in the Url to telegram

web music bot this is a telegram bot which get a site Url and sends all songs co

Arya Shabane 4 Apr 02, 2022
A Discord token stealer app written in Python 3.

Discord Token Stealer A Discord token stealer app written in Python 3. This version of the grabber only supports Windows. Features No local caching Tr

cankat 45 Jan 03, 2023
python3.5+ hubspot client based on hapipy, but modified to use the newer endpoints and non-legacy python

A python wrapper around HubSpot's APIs, for python 3.5+. Built initially around hapipy, but heavily modified. Check out the documentation here! (thank

Jacobi Petrucciani 140 Dec 21, 2022
The system to host your files on the Discord application

Distorage The system to host your files on the Discord application Documentation Documentation Distorage How to use the package You can install it wit

6 Jun 27, 2022
Projeto de estudantes do primeiro período do CIn - UFPE voltado para a criação de um sistema interativo no fechamento da disciplina IF669 - Introdução a Programação.

Projeto Game: Dona da Lua Alunos: Beatriz Férre Clara Kenderessy Matheus Silva Rafael Baltar Roseane Oliveira Samuel Marsaro Sinopse O Cebolinha apron

Maria Clara Kenderessy 5 Dec 20, 2021