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
A modular Telegram Python bot running on python3 with a sqlalchemy database.

Nao Tomori Robot Found Me On Telegram As Nao Tomori 🌼 A modular Telegram Python bot running on python3 with a sqlalchemy database. How to setup/deplo

Stinkyproject 1 Nov 24, 2021
AI-El-Yazisini-Tanima - Fotoğraflardaki El Yazını Yapay Zeka İle Otomatik Tanıma Yazılımı

AI-El Yazısını Tanıma Fotoğraflardaki El Yazını Yapay Zeka İle Otomatik Tanıma Yazılımı Amaç : Birden fazla makine öğrenmesi modelini bir arada kullan

Özgür Tokay 3 Mar 02, 2022
A discord bot that moderates your server!

Staff Bot para Discord O que é? É um bot que modera o seu servidor no Discord, apagando mensagens indesejadas que os usuários mandem! Como usar Primei

Isac Gonçalves Cunha 3 Oct 07, 2021
Python + AWS Lambda Hands OnPython + AWS Lambda Hands On

Python + AWS Lambda Hands On Python Criada em 1990, por Guido Van Rossum. "Bala de prata" (quase). Muito utilizado em: Automatizações - Selenium, Beau

Marcelo Ortiz de Santana 8 Sep 09, 2022
Cleaning Tiktok Hacks With Python

Cleaning Tiktok Hacks With Python

13 Jan 06, 2023
A telegram bot to track whales activities on multiple blockchains.

Telegram Bot : Whale Watcher A straightforward telegram bot written in python to track whales activity on multiple blockchains, using whale-alert API

Laurenz Bougan 1 Dec 10, 2021
Skyscanner Python SDK

Skyscanner Python SDK Important As of May 1st, 2020, the project is deprecated and no longer maintained. The latest update in v1.1.5 includes changing

Skyscanner 118 Sep 23, 2022
A mood based crypto tracking application.

Crypto Bud - API A mood based crypto tracking application. The main repository is private. I am creating the API before I connect everything to the ma

Krishnasis Mandal 1 Oct 23, 2021
Adds a new git subcommand named "ranch".

Git Ranch This script adds ranch, a new subcommand for git that makes it easier to order 1 Gallon of Kraft Ranch Salad Dressing from Amazon. Installat

Austin T Schaffer 8 Jul 06, 2022
Streaming Finance Data with AWS Lambda

A data pipeline consisting of an AWS lambda function reading data from yfinance API, an AWS Kinesis stream to receive & store data in S3 buckets and AWS Glue crawler & Athena to run SQL queries.

Aarif Munwar Jahan 4 Aug 30, 2022
Query Amalgamator over StackOverflow and YouTube

QASY Query Amalgamator over StackOverflow and YouTube Decription A software you can use to save your valuable time of googling the errors you encounte

1 Nov 07, 2021
YARSAW is an Async Python API Wrapper for the Random Stuff API.

Yet Another Random Stuff API Wrapper - YARSAW YARSAW is an Async Python API Wrapper for the Random Stuff API. This module makes it simpler for you to

Bruce 6 Mar 27, 2022
A collection of scripts to steal BTC from Lightning Network enabled custodial services. Only for educational purpose! Share your findings only when design flaws are fixed.

Lightning Network Fee Siphoning Attack LN-fee-siphoning is a collection of scripts to subtract BTC from Lightning Network enabled custodial services b

Reckless_Satoshi 14 Oct 15, 2022
An App to get Ko-Fi payment updates on Telegram.

Deployments. Heroku.com 🚀 Replit.com 🌀 Make sure your app runs 24*7 Zeet.co 💪 Use this :~ Get Bot token from @botfather 🤖 Get ID where you want to

Jainam Oswal 16 Nov 12, 2022
Copier template for solving Advent of Code puzzles with Python

Advent of Code Python Template for Copier This template creates scaffolding for one day of Advent of Code. It includes tests and can download your per

Geir Arne Hjelle 6 Dec 25, 2022
Create a Neo4J graph of users and roles trust policies within an AWS Organization.

AWS_ORG_MAPPER This tool uses sso-oidc to authenticate to the AWS organization. Once authenticated the tool will attempt to enumerate all users and ro

Ruse 24 Jul 28, 2022
WakeNote is a tool that hides notifications from you until you confirm you want to read them, with technology to help prevent the reading of depressing messages first thing in the morning.

By: Seanpm2001, Et; Al. Top README.md Read this article in a different language Sorted by: A-Z Sorting options unavailable ( af Afrikaans Afrikaans |

Sean P. Myrick V19.1.7.2 3 Oct 21, 2022
Information about the weather in a city written using Python

Information about the weather in a city Enter the desired city Climate information of the target city This program is written using Python programming

Amir Hussein Sharifnezhad 4 Nov 17, 2021
基于nonebot2的twitter推送插件

HanayoriBot(Twitter插件) ✨ 基于NoneBot2的Twitter推送插件,自带百度翻译接口 ✨ 简介 本插件基于NoneBot2与go-cqhttp,可以及时将Twitter用户的最新推文推送至群聊,并且自带基于百度翻译的推文翻译接口,及时跟进你所关注的Vtuber的外网动态。

鹿乃まほろ / Mahoro Kano 16 Feb 12, 2022
Catware - A powerful grabber with a built in bot control system

catware A powerful grabber with a built in bot control system PLEASE NOTE THAT I

4 Feb 04, 2022